【bzoj 1478: Sgu282 Isomorphism】【搜索+Polya定理】

题意

有一个 n n n个点的无向完全图,要对每条边染上 m m m种颜色的一种,问有多少种不不同的染色方案。两种染色方案同构当且仅当可以通过对其中一个图的点重标号后使其与另一个图完全相同。
n ≤ 53 , m ≤ 1000 , p n\le53,m\le1000,p n53,m1000,p为大于 n n n的素数。

分析

首先置换肯定有 n ! n! n!种,但我们无法枚举每种置换。注意到若两个置换中环的构成相同,则这两种置换的不动点的数目相同。要枚举不同环的构成只需要枚举 n n n的整数划分就好。
我们可以直接用Polya定理计算等价类数量,也就是 L = 1 ∣ G ∣ ∑ g ∈ G m c ( g ) L=\frac{1}{|G|}\sum_{g\in G}m^{c(g)} L=G1gGmc(g)其中 c ( g ) c(g) c(g)表示置换 g g g下的不动点数量。
假设置换中有 s k s_k sk个大小为 k k k的环,那么符合的置换数量为 n ! ∏ s k ! ∗ k s k \frac{n!}{\prod s_k!*k^{s_k}} sk!kskn!
这是因为先要把 1 1 1 n n n分配给每个环,然后一个大小为 k k k的环的方案为 ( k − 1 ) ! (k-1)! (k1)!,且所有大小相同的环是无序的,所以还要除以一个 s k ! s_k! sk!
设第 i i i个环的大小为 c i c_i ci,则该置换的不动点数量为 ∏ i m ⌊ c i 2 ⌋ ∏ i &lt; j m g c d ( c i , c j ) \prod_{i}m^{\lfloor\frac{c_i}{2}\rfloor}\prod_{i&lt;j}m^{gcd(c_i,c_j)} im2cii<jmgcd(ci,cj)
这是因为对于一个大小为 k k k的环,两点之间劣弧的不同长度有 ⌊ k 2 ⌋ \lfloor\frac{k}{2}\rfloor 2k种。而对于两个大小分别为 p p p q q q的环,在两个环上分别取一个点,两个点模 g c d ( p , q ) gcd(p,q) gcd(p,q)意义下的差值共有 g c d ( p , q ) gcd(p,q) gcd(p,q)种,且差值相等的两点之间的连边颜色必然相同。

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>

typedef long long LL;

const int N=60;

int n,m,MOD,s[N],c[N],tot,jc[N],ans;

int ksm(int x,int y)
{
	int ans=1;
	while (y)
	{
		if (y&1) ans=(LL)ans*x%MOD;
		x=(LL)x*x%MOD;y>>=1;
	}
	return ans;
}

int gcd(int x,int y)
{
	if (!y) return x;
	else return gcd(y,x%y);
}

void calc(int mx)
{
	int w=1;
	for (int i=1;i<=tot;i++) s[c[i]]++,w=(LL)w*c[i]%MOD;
	for (int i=1;i<=mx;i++) if (s[i]) w=(LL)w*jc[s[i]]%MOD,s[i]=0;
	int t=0;
	for (int i=1;i<=tot;i++)
	{
		t+=c[i]/2;
		for (int j=i+1;j<=tot;j++) t+=gcd(c[i],c[j]);
	}
	w=(LL)ksm(w,MOD-2)*ksm(m,t)%MOD;
	(ans+=w)%=MOD;
}

void dfs(int x,int ret)
{
	if (!ret) {calc(x-1);return;}
	if (ret<x) return;
	if (x==ret) c[++tot]=x,dfs(x+1,0),tot--;
	else
	{
		dfs(x+1,ret);
		int tmp=tot;
		for (int i=x;i<=ret;i+=x) c[++tot]=x,dfs(x+1,ret-i);
		tot=tmp;
	}
}

int main()
{
	scanf("%d%d%d",&n,&m,&MOD);
	jc[0]=1;
	for (int i=1;i<=n;i++) jc[i]=(LL)jc[i-1]*i%MOD;
	dfs(1,n);
	printf("%d\n",ans);
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值