P4128 [SHOI2006]有色图

传送门

数学渣渣看题解看得想死Ծ‸Ծ

首先发现这玩意儿看着很像polya定理

[L=frac{1}{|G|}sum_{iin G}m^{w(i)} ]

然而polya定理只能用来求点的置换,边的置换是布星的

于是我们考虑一个点的置换,把它写成若干循环的乘积((a_1,a_2,..)(b_1,b_2,...)...)

1.对于不在同一个循环里的点,比方说一条边((a_1,b_1)),那么和它在同一个循环的边有(((a_1,b_1),(a_2,b_2),...))(a)的循环节为(l_1)(b)的循环节为(l_2),那么这个边的循环的循环节长度就是(lcm(l_1,l_2)),而总共的边数为(l_1*l_2),那么循环的个数就是(frac{l_1*l_2}{lcm(l_1,l_2)}=gcd(l_1,l_2))

2.对于在同一个循环内的点,设(a)的循环节长度为(l_1)

如果(l_1)长度为奇数,那么循环的长度就是(l_1),总共有(C_{l_1}^{2})条边,那么循环的个数就是(frac{l-1}2)

如果(l_1)长度为偶数,除了上面的情况之外,还有一种很gg的情况就是比方说((a_1,a_{l_1/2+1}))的边所在的循环,这个循环的长度是(l_1/2),占的边数为(l_1/2),除此之外其他的情况都是一样的,所以循环的个数就是(frac{frac{l(l-1)}{2}-frac{l}2}{l}+1=frac l 2)

那么,总共的边的置换就是

[c=sum_{i=1}^klfloorfrac{l_i}{2} floor+sum_{i=1}^ksum_{j=i+1}^ngcd(l_i,l_j) ]

然而如果直接枚举的话复杂度是带一个感叹号的……然而我们只需要知道所有的(l_i)就行了,而不需要知道循环里具体是什么数字……所以会T就是我们知道的太多了

于是我们可以枚举(l_i),为了不重不漏保证(l_i)不降,先考虑如果(l_i)互不相同的话有多少种方案。我们可以这样理解,枚举(n)个数字的全排列,然后按(l_i)从左到右依次分组,那么这样肯定就是一个置换了……然后对于其中的每一个循环((a_1,a_2,...,a_l))来说,((a_2,a_3,...,a_l,a_1)...)之类的其实是跟它一样的,也就是说每个循环有(l_i)个同构的,所以要除掉,那么方案数就是

[S=frac{n!}{prod_{i=1}^k l_i} ]

然而现在问题是(l_i)有可能会相等,如果按上面那样考虑的话有可能会有两个(l_i)相等的循环被算到不同的里面了……所以还要设(B_i)(l_j==i)的个数,然后除掉他们中间排列的个数,那么方案数应该是

[S=frac{n!}{prod_{i=1}^k l_i*B_i!} ]

然后dfs暴力找(l_i)即可

最后就是

[Ans=Ans+S*m^c ]

之后就是抄代码了

//minamoto
#include<bits/stdc++.h>
#define int long long
#define fp(i,a,b) for(register int i=a,I=b+1;i<I;++i)
#define fd(i,a,b) for(register int i=a,I=b-1;i>I;--i)
using namespace std;
const int N=105;
int ans,P,n,m,fac[N],rec[N];
int gcd(int x,int y){return y?gcd(y,x%y):x;}
int ksm(int x,int y){
	int res=1;
	for(;y;y>>=1,x=x*x%P)if(y&1)res=res*x%P;
	return res;
}
void calc(int x){
	int sum=0,mul=1,now=1;
	fp(i,1,x)sum+=rec[i]/2;
	fp(i,1,x)fp(j,i+1,x)sum+=gcd(rec[i],rec[j]);
	fp(i,1,x)(mul*=rec[i])%=P;
	fp(i,2,x){
		if(rec[i]!=rec[i-1])(mul*=fac[now])%=P,now=0;
		++now;
	}(mul*=fac[now])%=P,mul=fac[n]*ksm(mul,P-2)%P;
	(ans+=mul*ksm(m,sum)%P)%=P;
}
void dfs(int k,int x,int s){
	if(!x)calc(k-1);if(x<s)return;
	fp(i,s,x)rec[k]=i,dfs(k+1,x-i,i);
}
signed main(){
//	freopen("testdata.in","r",stdin);
	scanf("%lld%lld%lld",&n,&m,&P),fac[0]=1;
	fp(i,1,n)fac[i]=fac[i-1]*i%P;
	dfs(1,n,1);(ans*=ksm(fac[n],P-2))%=P;
	printf("%lld
",ans);return 0;
}
原文地址:https://www.cnblogs.com/bztMinamoto/p/10054795.html