[SHOI2006]有色图
假题,该题并没有色图;
考虑\(\rm Burnside\)引理,一类元素在某个置换群下的等价类个数等于\(\displaystyle \frac{1}{|G|}\sum_{g\in G}M(g)\),\(M(g)\)是置换\(g\)下的不动点个数;根据\(\rm Polya\)定理,我们知道不动点个数跟置换环的个数息息相关,在染色问题时一个置换内的颜色应该是相等的;
考虑一组置换\((a_1,a_2,\dots a_n)\),设该置换在\(i\)向\(a_i\)连边后形成了\(m\)个环,大小分别为\(b_1,b_2,\dots b_m\),显然有\(\displaystyle \sum_{i=1}^m b_i=n\);对于这道题我们要求是边的等价类,那就考虑从点的置换推到边的置换;
考虑点置换形成大小为\(b_1,b_2,\dots b_m\)的\(m\)个置换环时边的置换环的个数;我们分成两情况讨论一下;
-
两点在同一个置换环中:考虑一条边\((x,y)\)(\(x,y\)是在环中的编号),经过置换后的这条边变成了\(((x+1)\%b_i,(y+1)\%b_i)\),也就是有一条边从\((x,y)\)连向\(((x+1)\%b_i,(y+1)\%b_i)\),不难发现\(|x-y|\)没有变化;于是我们可以根据一条边两点编号的差的绝对值分到不同的置换环中;于是长度为\(b_i\)的点置换中能形成\(\lfloor \frac{b_i}{2}\rfloor\)。
-
两点不在同一个置换环中:设\(x\)在置换\(b_i\)中,\(y\)在置换\(b_j\)中,则\((x,y)\)变成了\(((x+1)\% b_i,(y+1)\% b_j)\),我们于是\((x,y)\)在一个长度为\({\rm lcm} (b_i,b_j)\)的置换中,一共有\(b_i\times b_j\)个点,于是共有\(\frac{b_ib_j}{{\rm lcm}(b_i,b_j)}=\gcd(b_i,b_j)\)个置换环;
所以共有\(\displaystyle{\sum_{i=1}^m\lfloor \frac{b_i}{2}\rfloor+\sum_{i=1}^m\sum_{j=i+1}^m \gcd(b_i,b_j)}\)个置换环;考虑直接按照整数划分分方式来搜\(b\)序列,之后就能算出置换环的个数了;
由于我们搜的是整数划分,于是需要考虑一组\(b\)出现了几次;首先每一个\(b_i\)内部是一个环排列,于是要乘上\(\prod (b_i-1)!\);之后我们把\(n\)个元素分成\(m\)组是一个重复元素的排列问题,需要乘上\(\frac{n!}{\prod b_i!}\);但是这样还有可能算重,我们发现算重的情况是对于两个大小相同的\(b_i\)谁排在前面造成的,我们规定大小相同的置换环按照其中最小的元素排列,这样还需要除以\(\prod c_i!\),\(c_i\)是大小为\(i\)的置换环的个数。
代码
#include<bits/stdc++.h>
#define re register
#define LL long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
inline int read() {
char c=getchar();int x=0;while(c<'0'||c>'9') c=getchar();
while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
const int maxn=54;int n,m,mod,ans;
int Gcd[maxn][maxn],a[maxn],fac[maxn],ifac[maxn],num[maxn];
inline int qm(int a) {return a>=mod?a-mod:a;}
inline int ksm(int a,int b) {
int S=1;for(;b;b>>=1,a=1ll*a*a%mod)if(b&1)S=1ll*S*a%mod;return S;
}
int gcd(int a,int b) {
if(!b)return a;if(Gcd[a][b])return Gcd[a][b];
return Gcd[a][b]=gcd(b,a%b);
}
void dfs(int x,int s,int res) {
if(!res) {
int tot=0,cnt=1,sum=0;
for(re int i=1;i<x;i++)tot+=(a[i]>>1);
for(re int i=1;i<x;i++)
for(re int j=i+1;j<x;j++) tot+=gcd(a[i],a[j]);
for(re int i=1;i<=n;i++)cnt=1ll*cnt*ifac[num[i]]%mod;
for(re int i=1;i<x;i++)cnt=1ll*cnt*ifac[a[i]]%mod*fac[a[i]-1]%mod;
ans=qm(ans+1ll*cnt*ksm(m,tot%(mod-1))%mod);
return;
}
for(re int i=s;i<=res;i++)a[x]=i,num[i]++,dfs(x+1,i,res-i),num[i]--;
}
int main() {
n=read(),m=read(),mod=read();fac[0]=ifac[0]=1;
for(re int i=1;i<=n;i++)fac[i]=1ll*fac[i-1]*i%mod;
ifac[n]=ksm(fac[n],mod-2);
for(re int i=n-1;i;i--)ifac[i]=1ll*ifac[i+1]*(i+1)%mod;
dfs(1,1,n);printf("%d\n",ans);
return 0;
}