解题:SHOI 2006 有色图

题面

本质上是在对边求置换,然后每个循环里涂一样的颜色,但是还是要点上入手,考虑每条边的两个端点是否在一个循环里

如果在一个循环里,那么当循环长度$len$为奇数时只有转一整圈才行,而边的总数是$\frac{len(len-1)}{2}$,所以有$\frac{\frac{len(len-1)}{2}}{len}=\left\lfloor\frac{len}{2}\right\rfloor$个循环节;当循环长度为偶数时除了上面这种情况正对的每对点旋转$\frac{len}{2}$就可以,所以也是有$\frac{\frac{len(len-1)}{2}-frac{len}{2}}{len}+\frac{\frac{len}{2}}{\frac{len}{2}}=\left\lfloor\frac{len}{2}\right\rfloor$个循环节

如果不在一个循环里,那循环节数量就是套路的两者所在循环长度的GCD

那么暴搜数的拆分得到每种点置换就可以求出答案了,具体的,在总共$n!$种点置换中,每个循环节$i$自己做圆排列除去$len[i]$,同时每个长度的循环节之间的排列也要除去,所以设$cnt[i]$表示长度为$i$的循环节的数量,那么满足拆分$len[1],len[2],len[3].....len[m]$的点置换的方案数就是

$\frac{n!}{\prod\limits_{i=1}^mlen[i]\prod\limits_{i=1}^mcnt[i]!}$

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 const int N=64;
 6 int n,m,p,ans,fac[N],inv[N],len[N];
 7 int GCD(int a,int b)
 8 {
 9     return b?GCD(b,a%b):a;
10 }
11 int Qpow(int x,int k)
12 {
13     if(k==1) return x;
14     int tmp=Qpow(x,k/2);
15     return k%2?1ll*tmp*tmp%p*x%p:1ll*tmp*tmp%p;
16 }
17 int Inv(int x)
18 {
19     return Qpow(x,p-2);
20 }
21 void Calc(int cnt)
22 {
23     int pts=1,bas=1,lst=1,sum=0;
24     for(int i=1;i<=cnt;i++) 
25     {
26         bas=1ll*bas*len[i]%p;
27         if(len[i]!=len[lst])     
28             bas=1ll*bas*fac[i-lst]%p,lst=i;
29     }
30     bas=1ll*bas*fac[cnt-lst+1]%p;
31     pts=1ll*fac[n]*Inv(bas)%p;
32     for(int i=1;i<=cnt;i++)
33     {
34         sum+=len[i]/2;
35         for(int j=i+1;j<=cnt;j++)
36             sum+=GCD(len[i],len[j]);
37     }
38     ans=(ans+1ll*pts*Qpow(m,sum)%p)%p;
39 }
40 void DFS(int cnt,int mnn,int mxx)
41 {
42     if(!mxx) Calc(cnt);
43     else
44         for(int i=mnn;i<=mxx;i++)
45             len[cnt+1]=i,DFS(cnt+1,i,mxx-i);
46 }
47 int main()
48 {
49     scanf("%d%d%d",&n,&m,&p);
50     fac[0]=inv[0]=1;
51     for(int i=1;i<=n;i++) fac[i]=1ll*fac[i-1]*i%p;
52     inv[n]=Inv(fac[n]);
53     for(int i=n-1;i;i--) inv[i]=1ll*inv[i+1]*(i+1)%p;
54     DFS(0,1,n),printf("%lld",1ll*ans*inv[n]%p);
55     return 0;
56 }
View Code

 

转载于:https://www.cnblogs.com/ydnhaha/p/10307555.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值