题解:P4128[SHOI2006] 有色图&P4727[HNOI2009] 图的同构计数
考虑将 P4727 转化为给完全图染色,染成存在和不存在两种颜色,于是变成了 P4128 的 \(m=2\) 情况。
首先根据 Burnside 引理,有:
这里的每一个置换 \(g\) 也就是原图的点的置换,共有 \(n!\) 个。
考虑求每个 \(|X^g|\),根据 Pólya 定理,我们只需求出此时边的等价类数量 \(c\),\(|X^g|\) 即为 \(m^c\)。可以将每个置换拆成若干个循环(轮换),即循环中的每个点的置换都是循环中的下一个点。设第 \(i\) 个循环的大小为 \(b_i\)。
我们先考虑两端点在同一循环内的边的等价类数量,如果将循环排列在一个圆上,则每次置换都会使每条边的端点顺时针前进一个点,因而跨越的点的数量相同的边构成一个等价类,所以等价类的数量是 \(\lfloor\frac{b_i}2\rfloor\)。
对于两端点不在同一循环内的边,设两端点分别在第 \(i\) 和 \(j\) 个循环内。我们仍然将两个循环分别排列在圆上,则经过 \(\operatorname{lcm}(b_i,b_j)\) 次置换后,这条边会再次回到初始位置,因而每个等价类的大小都是 \(\operatorname{lcm}(b_i,b_j)\),等价类的数量也就是边数 \(b_ib_j\) 除以 \(\operatorname{lcm}(b_i,b_j)\),即 \(\gcd(b_i,b_j)\)。
综上,对于每个置换 \(g\),假设其可以拆成 \(k\) 个置换,则 \(|X^g|=\sum_{i=1}^k\lfloor\frac{b_i}2\rfloor+\sum_{j=1}^{i-1}\gcd(b_i,b_j)\),但如果直接计算的话时间复杂度至少是 \(O(n!)\) 的,无法接受。
考虑 \(b\) 事实上是 \(n\) 的整数拆分,因而我们可以对于每个整数拆分进行计算。对于一个拆分,考虑它对应多少种置换,首先所有点有 \(n!\) 种排列,但因为 \(|G|=n!\),所以这个贡献被 \(\frac1{|G|}\) 消掉了。每个拆分内部可以以任意一个位置为循环的起始,所以要除以 \(\prod b_i\)。而长度相同的循环之间是可以互换的,设每种长度相同的循环有 \(c\) 个,则还要再除以 \(\prod c!\)。综上,每个拆分的贡献为:
我们通过 DFS 将所有拆分的贡献加到一起即可,时间复杂度比较神秘但能过。
Code(P4128):
#include<iostream>
#define rep(i,l,r) for(int i=(l);i<=(r);i++)
#define per(i,l,r) for(int i=(l);i>=(r);i--)
using namespace std;
typedef long long ll;
inline int gcd(int a,int b){
return !b?a:gcd(b,a%b);
}
int in,im,ip;
ll st[60],top,c1=1,c2,ans;
inline ll qpow(ll a,ll b){
ll res=1;
while(b){
if(b&1)res=res*a%ip;
a=a*a%ip;
b>>=1;
}
return res;
}
inline void dfs(int lf,int mx,int c){
if(!lf){
ans=(ans+qpow(c1,ip-2)*qpow(im,c2))%ip;
return;
}
ll t1=c1,t2=c2;
top++;
rep(v1,1,mx){
st[top]=v1;
c2=t2+v1/2;
rep(v2,1,top-1)c2+=gcd(st[v2],v1);
c1=t1*v1%ip;
if(top>1&&st[top-1]==v1)c1=c1*(c+1)%ip;
dfs(lf-v1,min(lf-v1,v1),(top>1&&st[top-1]==v1)?c+1:1);
}
top--;
}
int main(){
cin>>in>>im>>ip;
dfs(in,in,0);
cout<<ans<<endl;
return 0;
}

浙公网安备 33010602011771号