题解 P4128 【[SHOI2006] 有色图】
AsunderSquall · · 题解
update:修复了几个笔误
置换群论基础练习题。
首先我们发现这个
这个式子看着很复杂,事实上可以在 dfs 的时候写得好一些,然后在 dfs 过程中就求出来。
然后就可以用 Burnside 引理了。
考虑怎么算 这样一个序列的不动点数量。
考虑一条边,分两种情况讨论。
- 这条边在循环内部
那么每个循环贡献的不动点数量为\lfloor \dfrac {a_i} 2 \rfloor - 这条边在两个循环之间
那么所有循环贡献的不动点数量为\sum_{1\le i<j\le m} \gcd(a_i,a_j)
那么最后答案为
我们知道
所以答案为
然后就做完了。
在代码中用 dif 表示
same 表示
cnt 表示上一个数已经重复出现了几遍。
lft 表示在序列中还剩下多少没有被划入置换中。
mx 表示序列的上一个数是什么,同时也是序列的下一个数的最大值限制。
代码:(变量名已经改为和题解相同)
#include<bits/stdc++.h>
#define rd(x) x=read()
#define ri register int
#define int long long
using namespace std;
int mod=1e9+7;
const int N=100;
inline int read(){int x=0,f=1;char ch=getchar();while((ch>'9' || ch<'0')){if(ch=='-') f=-1;ch=getchar();}while('0'<=ch && ch<='9') x=x*10+(ch^48),ch=getchar();return x*f;}
inline int ksm(int x,int y=mod-2,int z=mod){int ret=1;while (y){if (y&1LL) ret=ret*x%mod;x=x*x%mod;y>>=1LL;}return ret;}
int n,T,k,ans;
int inv[N],fac[N],ifc[N];
void Init(int n){
inv[1]=1;for (ri i=2;i<=n;i++)inv[i]=inv[mod%i]*(mod-mod/i)%mod;
fac[0]=1;for (ri i=1;i<=n;i++)fac[i]=fac[i-1]*i%mod;
ifc[0]=1;for (ri i=1;i<=n;i++)ifc[i]=ifc[i-1]*inv[i]%mod;}
int a[N],m,dif,same=1;
void dfs(int lft,int mx,int cnt)
{
if (!lft){ans=(ans+ksm(k,dif)*same)%mod;return;}
int Dif=dif,Same=same;
for (int i=1;i<=mx;i++)
{
a[++m]=i;
dif=Dif+i/2;
for (int j=1;j<m;j++) dif+=__gcd(a[j],i);
same=Same*inv[i]%mod;
if (i==a[m-1]) same=same*inv[cnt+1]%mod,dfs(lft-i,min(lft-i,i),cnt+1);
else dfs(lft-i,min(lft-i,i),1);
m--;
}
}
signed main()
{
rd(n);rd(k);rd(mod);
Init(n+5);dfs(n,n,0);
cout<<ans<<endl;
}
在文章最后放两张鏼图
点击链接查看
再放一张