传送门
题解:
目前是洛谷上面非打表代码的rk1。
很显然我们考虑点的 n ! n! n!种置换。
显然对应着边的 n ! n! n!种置换,对于每条边的出现与否,我们可以看作染色为黑白。
显然染色方案用Polya算一下,现在的问题是每种点置换中有多少种边等价类。
首先考虑一条边 ( u , v ) (u,v) (u,v),如果 u , v u,v u,v在同一个循环中,显然所有在循环中距离相等的点的连边在同一个等价类中,形式化地说,一个长度为 d d d的循环,两个端点都在其内部的边会形成 ⌊ d 2 ⌋ \lfloor\frac{d}{2}\rfloor ⌊2d⌋个等价类。
接下来还是考虑一条边 ( u , v ) (u,v) (u,v),如果 u , v u,v u,v不在同一个循环中,我们考虑将 u u u在循环中前进一步的同时 v v v在循环中也前进一步,则回到 u , v u,v u,v状态之前一共会遍历 l c m ( a , b ) lcm(a,b) lcm(a,b)个不同的边,其中 a , b a,b a,b分别是两个循环的长度,则这两个循环之间的边等价类有 a b / l c m ( a , b ) = g c d ( a , b ) ab/lcm(a,b)=gcd(a,b) ab/lcm(a,b)=gcd(a,b)个。
发现边等价类个数只和循环大小有关系。
直接暴力枚举有序正整数拆分。则一个有序正整数拆分对应的原图有 n ! ∏ L i ∏ c n t [ i ] ! \dfrac{n!}{\prod L_i\prod cnt[i]!} ∏Li∏cnt[i]!n!
其中 L i L_i Li是拆分中第 i i i个数的大小,也就是第 i i i个循环的长度,其实就是考虑固定开头就行了,后面 c n t [ i ] cnt[i] cnt[i]是长度为 i i i的循环个数,考虑所有 c n t [ i ] cnt[i] cnt[i]个循环互换位置可以得到上面的式子。
等价类个数和对应的原图方案数可以在枚举拆分的同时算出来。
最后需要除掉一个 n ! n! n!,那么可以在算方案数的时候直接不算 n ! n! n!
代码:
#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const
using std::cerr;
using std::cout;
cs int mod=997;
inline int add(int a,int b){a+=b-mod;return a+(a>>31&mod);}
inline int dec(int a,int b){a-=b;return a+(a>>31)&mod;}
inline int mul(int a,int b){int r=a*b;return r>=mod?r%mod:r;}
inline int power(int a,int b,int res=1){
for(;b;b>>=1,a=mul(a,a))(b&1)&&(res=mul(res,a));
return res;
}
inline void Inc(int &a,int b){a+=b-mod;a+=a>>31&mod;}
inline void Dec(int &a,int b){a-=b;a+=a>>31&mod;}
inline void Mul(int &a,int b){a=mul(a,b);}
int inv[mod];
inline void init_inv(){
inv[0]=inv[1]=1;
for(int re i=2;i<mod;++i)inv[i]=mul(mod-mod/i,inv[mod%i]);
}
cs int N=65;
int n;
int g[N][N],pw[N*N];
int b[N],tot,ans;
int typ[N],t;
int cnt[N];
void dfs(int last,int rest,int coef,int sum){
if(rest==0){
Inc(ans,mul(coef,pw[sum]));
return ;
}
for(int re i=std::min(last,rest);i;--i){
int c=coef,s=sum;
s+=i/2;
for(int re k=1,j;k<=t;++k){
j=typ[k];
s+=cnt[j]*g[i][j];
}
++cnt[i];if(i!=last)typ[++t]=i;
Mul(c,mul(inv[i],inv[cnt[i]]));
dfs(i,rest-i,c,s);
--cnt[i];if(i!=last)--t;
}
}
signed main(){
#ifdef zxyoi
freopen("graph.in","r",stdin);
#endif
scanf("%d",&n);
init_inv();
pw[0]=1;for(int re i=1;i<N*N;++i)pw[i]=add(pw[i-1],pw[i-1]);
for(int re i=1;i<=n;++i)
for(int re j=1;j<=n;++j)g[i][j]=std::__gcd(i,j);
dfs(n+1,n,1,0);
cout<<ans<<"\n";
return 0;
}