BZOJ1488 [HNOI2009]图的同构

来源:互联网 发布:什么软件收二手手机 编辑:程序博客网 时间:2024/06/08 01:57

我们可以把每条边存在或者不存在看成是黑和白两种颜色

然后这个题就充斥着一股ploya定理的气息

但是有关边的置换太蛋疼,我们考虑把点的置换对应到边上

找找规律,我们发现对于一个点的置换,如果其存在一个循环节大小为x,那么这x个点的边之间会形成x/2个循环节,如果其存在两个循环节大小分别为x和y,那么两个循环节里的点之间的边会形成gcd(x,y)个循环节

我们考虑爆搜点置换,每次枚举当前最大的循环节多大,有多少个(不要问我为什么这么搜复杂度是对的,我并不懂)

那么假设当前有cnt种循环节,每种的大小是v[i],每种有t[i]个,那么这样的置换一共就有n!/v[1]v[2]v[3]...v[cnt]t[1]!t[2]!t[3]!...t[cnt]!种

然后就可以算了

#include<iostream>#include<cstdio>#include<cstdlib>#include<cstring>#include<ctime>#include<cmath>#include<algorithm>#include<iomanip>#include<vector>#include<map>#include<set>#include<bitset>#include<queue>#include<stack>using namespace std;#define MAXN 70#define MAXM 1010#define INF 1000000000#define MOD 997#define eps 1e-8#define ll long longint fac[MAXN],ine[MAXN],inv[MAXN];int n;int v[MAXN],t[MAXN];int tot;int ans;int gcd(int x,int y){return !y?x:gcd(y,x%y);}int mi(int x,int y){int re=1;while(y){if(y&1){(re*=x)%=MOD;}(x*=x)%=MOD;y>>=1;}return re;}void dfs(int x,int rem){int i,j;if(!rem){int tmp=fac[n];int cnt=0;for(i=1;i<=tot;i++){(tmp*=ine[t[i]]%MOD)%=MOD;for(j=1;j<=t[i];j++){(tmp*=inv[v[i]])%=MOD;}}for(i=1;i<=tot;i++){cnt+=t[i]*(v[i]/2);cnt+=t[i]*(t[i]-1)/2*v[i];for(j=1;j<i;j++){cnt+=t[i]*t[j]*gcd(v[i],v[j]);}}(ans+=tmp*mi(2,cnt))%=MOD;return ;}if(x>rem){return ;}dfs(x+1,rem);for(i=1;i*x<=rem;i++){v[++tot]=x;t[tot]=i;dfs(x+1,rem-i*x);tot--;}}int main(){int i;fac[0]=ine[0]=inv[0]=inv[1]=1;for(i=1;i<MAXN;i++){fac[i]=fac[i-1]*i%MOD;}for(i=2;i<MAXN;i++){inv[i]=(MOD-MOD/i)*inv[MOD%i]%MOD;}for(i=1;i<MAXN;i++){ine[i]=inv[i]*ine[i-1]%MOD;}scanf("%d",&n);dfs(1,n);(ans*=ine[n])%=MOD;printf("%d\n",ans);return 0;}/**/


0 0