[luogu4128][shoi2006]有色图

前言

计数题

题目相关

题目链接

题目大意

n n n个点的完全图,对边染色(颜色有 m m m种),求本质不同的染色方案数,答案对 p p p取模

数据范围

1 ≤ n ≤ 53 , 1 ≤ m ≤ 1000 , 1 ≤ p ≤ 1 0 9 1\le n\le53,1\le m\le1000,1\le p\le10^9 1n53,1m1000,1p109

题解

我们乍一看是染色问题,我们就想到了Polya定理
l = 1 ∣ G ∣ ∑ a i ∈ G k λ ( a i ) l=\frac{1}{|G|}\sum_{a_i\in G}k^{\lambda(a_i)} l=G1aiGkλ(ai)
我们可以用其式子来计算
但是我们发现染色的是边而不是点,而是边,所以我们计算的置换是对于边的
我们考虑先不枚举边置换,而是枚举点的置换,容易发现,一个点置换,其对应的边置换是唯一确定的
这样一来,就有了方便的枚举置换方式
我们发现,点的置换有 n ! n! n!种,我们发现 53 ! 53! 53!的复杂度显然不能接受
我们考虑不直接枚举所有点置换,而是枚举本质不同的点置换,本质相同的点置换,对应的边置换也是本质相同的
这样的话复杂度就是 n n n的整数划分了,随便写个程序就可以发现当 n = 53 n=53 n=53时,整数划分是 3 ∗ 1 0 5 3*10^5 3105级别的,目前看起来可以接受
code

#include
#include
#define rg register
template<typename T>inline void read(T&x){char cu=getchar();x=0;bool fla=0;while(!isdigit(cu)){if(cu=='-')fla=1;cu=getchar();}while(isdigit(cu))x=x*10+cu-'0',cu=getchar();if(fla)x=-x;}
template<typename T>inline void printe(const T x){if(x>=10)printe(x/10);putchar(x%10+'0');}
template<typename T>inline void print(const T x){if(x>=0)printe(x);else putchar('-'),printe(-x);}
int n,ans;
void dfs(const int t,const int h)
{
	if(t==0)ans++;
	else for(rg int i=h;i<=t;i++)dfs(t-i,i);
}
int main()
{
	read(n);
	dfs(n,1);
	print(ans);
	return 0;
}

我们考虑现在得到了一个整数划分,其有 k k k个循环,按循环长度排序后的第 i i i个循环长度为 L i L_i Li L 1 ≤ L 2 ≤ ⋅ ⋅ ⋅ ≤ L k L_1\le L_2\le···\le L_k L1L2Lk
我们思考对于这样一个点置换,其对应的边置换的循环数量(在Polya中要求的东西)
两个端点在同一个长度为 L L L的点循环内的情况:
我们发现,对于任意一条边,将其两边的点转动,这样就可以产生一个大小为 L L L的边循环
然后这个时候有个特例,当 L L L为偶数时,有一个大小为 L 2 \frac{L}{2} 2L的循环,即环上每组相对的点所组成的循环
即:
L L L为奇数时,循环数为 ( L 2 ) L = L ∗ ( L − 1 ) / 2 L = L − 1 2 \frac{\binom{L}{2}}{L}=\frac{L*(L-1)/2}{L}=\frac{L-1}2 L(2L)=LL(L1)/2=2L1
L L L为偶数时,循环数为 ( L 2 ) + L 2 L = L ∗ ( L − 1 ) / 2 + L / 2 L = L 2 \frac{\binom{L}{2}+\frac{L}{2}}{L}=\frac{L*(L-1)/2+L/2}{L}=\frac L2 L(2L)+2L=LL(L1)/2+L/2=2L
综上,这里的边循环数为 ⌊ L 2 ⌋ \left\lfloor\frac L2\right\rfloor 2L
两个端点分别在长度为 L a L_a La L b L_b Lb的点循环内的情况:
容易发现,这里的边循环长度为 l c m ( L a , L b ) lcm(L_a,L_b) lcm(La,Lb)
那么循环数量为 L a ∗ L b l c m ( L a , L b ) = g c d ( L a , L b ) \frac{L_a*L_b}{lcm(L_a,L_b)}=gcd(L_a,L_b) lcm(La,Lb)LaLb=gcd(La,Lb)

这样的话,对于一个枚举到的本质不同的点循环,其对Poyla定理中后面那个sigma的指数 C C C就为
C = ∑ i = 1 k ⌊ L i 2 ⌋ + ∑ i = 1 k ∑ j = i + 1 k ( L i , L j ) C=\sum_{i=1}^k\left\lfloor\frac {L_i}2\right\rfloor+\sum_{i=1}^k\sum_{j=i+1}^k(L_i,L_j) C=i=1k2Li+i=1kj=i+1k(Li,Lj)
我们也知道颜色数,现在还需要统计这种点循环的出现次数
容易发现,出现次数 S S S可以直接计算
我们设 T i T_i Ti为长度为 i i i L L L出现的次数(另外,定义 0 ! = 1 0!=1 0!=1
S = ∏ i = 1 k ( L i − 1 ) ! ( n − ∑ j = 1 i − 1 L j L i ) ∏ i = 1 n T i ! = n ! ∏ i = 1 n T i ! ∗ ∏ i = 1 k L i S=\frac{\prod_{i=1}^k(L_i-1)!\binom{n-\sum_{j=1}^{i-1}L_j}{L_i}}{\prod_{i=1}^{n}T_i!}=\frac{n!}{\prod_{i=1}^{n}T_i!*\prod_{i=1}^{k}L_i} S=i=1nTi!i=1k(Li1)!(Linj=1i1Lj)=i=1nTi!i=1kLin!
这样的话我们就能快速的求出 S S S
最终答案就可以算出来了
l = S ∗ m C n ! l=\frac{S*m^C}{n!} l=n!SmC

代码

这份代码为了方便理解,写的复杂度比较大,实际可以有更好的写法使程序跑得更快

#include
#include
#define rg register
typedef long long ll;
template<typename T>inline void read(T&x){char cu=getchar();x=0;bool fla=0;while(!isdigit(cu)){if(cu=='-')fla=1;cu=getchar();}while(isdigit(cu))x=x*10+cu-'0',cu=getchar();if(fla)x=-x;}
template<typename T>inline void printe(const T x){if(x>=10)printe(x/10);putchar(x%10+'0');}
template<typename T>inline void print(const T x){if(x>=0)printe(x);else putchar('-'),printe(-x);}
template <typename T> inline T gcd(const T a,const T b){if(!b)return a;return gcd(b,a%b);}
ll n,m,p,L[61],fac[61],ans,tim[61];
inline ll pow(ll x,ll y)
{
	ll res=1;
	for(;y;y>>=1,x=x*x%p)if(y&1)res=res*x%p;
	return res;
}
void dfs(const int step,const int t,const int h)
{
	if(t==0)
	{
		ll xs=0,xf=1;
		for(rg int i=1;i<step;i++)
		{
			xs+=L[i]>>1;
			for(rg int j=i+1;j<step;j++)xs+=gcd(L[i],L[j]);
			xf=xf*L[i]%p;
		}
		for(rg int i=1;i<=n;i++)xf=xf*fac[tim[i]]%p;
		ans+=pow(m,xs)*pow(xf,p-2)%p;
	}
	else for(rg int i=h;i<=t;i++)
	{
		L[step]=i;
		tim[i]++;
		dfs(step+1,t-i,i);
		tim[i]--;
	}
}
int main()
{
	read(n),read(m),read(p);
	fac[0]=1;for(rg int i=1;i<=60;i++)fac[i]=fac[i-1]*i%p;
	dfs(1,n,1);
	print(ans%p);
	return 0;
}

总结

代码非常的清真,良好的计数技巧配上Poyla定理就能解决这题
计数好难

你可能感兴趣的:(OI,Polya定理,Burnside引理,组合数学)