分析:
据说很板的Polya定理的题。。。
可能我学了假的Polya。。。
首先回顾一下Polya定理的内容:
对n个染色位置的本质不同的染色方案数(即不能通过置换得到另一染色方案):
这里的k是指一种置换, ck c k 指这种置换的循环数。
当然,这个基础公式很多时候并不实用,因此还有一种计算方式:
这个就更加粗暴了,p是某种置换的循环个数, ap a p 指循环个数为 p p 个的置换有多少个。
这题就需要这个公式。
现在这题最大的难点是:置换是在点上的,然而染色是在边上的。
因此问题就是:如何处理点置换与边置换的对应关系。
把边置换分为两部分来看:边的两端在同一个点循环内的,边的两端在不同的点循环内的。
第一种情况:
边循环大小为点循环大小的一半(向下取整)。
证明据说很直观:
枚举这个循环内的点置换
i∈[0,sum−1](sum表示点循环大小)
i
∈
[
0
,
s
u
m
−
1
]
(
s
u
m
表
示
点
循
环
大
小
)
那么当
i>⌊sum2⌋
i
>
⌊
s
u
m
2
⌋
时,就可以在
[0,⌊sum2⌋]
[
0
,
⌊
s
u
m
2
⌋
]
中找一个与它相同。
第二种情况:
这两个点循环之间的边循环大小,为两个点循环大小的GCD。
证明据说也很简单。。可以画图来证明?
附一个大佬的博客。。她已经写得非常清楚了
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#define SF scanf
#define PF printf
#define MAXN 65
#define MOD 997
using namespace std;
int fsp(int x,int y){
int res=1;
while(y){
if(y&1)
res=res*x%MOD;
x=x*x%MOD;
y>>=1;
}
return res;
}
int gcd(int x,int y){
if(y==0)
return x;
return gcd(y,x%y);
}
int a[MAXN],b[MAXN],fac[MAXN],ans,n,m;
void solve(int x,int sum,int cnt){
if(sum==0){
int res=0,add=1;
for(int i=1;i<=cnt;i++){
res+=(a[i]/2*b[i]+b[i]*(b[i]-1)/2*a[i])%(MOD-1);
add=add*fac[b[i]]%MOD*fsp(a[i],b[i])%MOD;
for(int j=i+1;j<=cnt;j++)
res+=b[i]*b[j]*gcd(a[i],a[j]);
res%=(MOD-1);
}
add=fac[n]*fsp(add,MOD-2)%MOD;
ans+=add*fsp(m,res)%MOD;
ans%=MOD;
return ;
}
if(x==0)
return ;
for(int i=1;i*x<=sum;i++){
a[cnt+1]=x;
b[cnt+1]=i;
solve(x-1,sum-i*x,cnt+1);
}
solve(x-1,sum,cnt);
}
int main(){
freopen("color.in","r",stdin);
freopen("color.out","w",stdout);
SF("%d%d",&n,&m);
fac[0]=1;
for(int i=1;i<=n;i++)
fac[i]=fac[i-1]*i%MOD;
solve(n,n,0);
ans*=fsp(fac[n],MOD-2);
ans%=MOD;
PF("%d",ans);
}