bzoj1488 图的同构

图的同构

题目描述
传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=1488

题解

还在搞OI的时候就看过这道题,看的一脸懵逼。
学了群论的相关知识再来看这题,还是一脸懵逼。
后来借助题解知道了做法。

求n个点完全不同的简单无向图的方案数。
对于每条边,存在与不存在等价于黑白染色。
因为涉及到点的置换,边的置换,所以考虑polya定理。

枚举点的置换群的点循环的循环大小
对于同一个点循环内的边循环的大小等于点循环大小/2
对于不同点循环内边循环大小等于两个点循环大小的gcd。
这个手玩一下应该看得出来为什么
所以sum=Σ2^(Σsize[i]/2+Σgcd(size[i],size[j]))

接下来求符合该循环集条件下的边置换的个数,即对应的点置换的个数。
tmp=n!/π( size[i])/(π num[i]!)
其中num[i]表示大小为size[i]的循环节的个数
除掉size[i]可以理解为圆形排列
除掉num[i]!是因为相同大小的循环节会出现重复计算

ans=Σsum*tmp/n!

题解参考的这份:
http://blog.csdn.net/wzq_qwq/article/details/48035455

然后这题也可以手完几个点然后去查OEIS(逃)

双倍经验 bzoj1815

代码

#include<bits/stdc++.h>
#define mod 997
#define N 65
using namespace std;
int n,ans,val[N],s[N],fac[N];

int Pow(int a,int b)
{
  int res=1;
  while(b)
  {
    if(b&1)res=res*a%mod;
    a=a*a%mod;b>>=1;
  }
  return res;
}

int gcd(int a,int b)
{
  if(!b)return a;
  return gcd(b,a%b);
}

void dfs(int x,int res,int num)
{
  if(!res)
  {
    int sum=0,tmp=1;
    for(int i=1;i<=x;i++)
    {
      sum+=s[i]*(s[i]-1)/2*val[i]+val[i]/2*s[i];
      for(int j=i+1;j<=x;j++)
        sum+=gcd(val[i],val[j])*s[i]*s[j];
    }
    for(int i=1;i<=x;i++)
      tmp=tmp*fac[s[i]]*Pow(val[i],s[i])%mod;
    tmp=Pow(tmp,mod-2)*fac[n]%mod;
    ans=(ans+Pow(2,sum)*tmp)%mod;
    return;
  }
  if(num>n)return;
  dfs(x,res,num+1);
  for(int i=1;i*num<=res;i++)
  {
    val[x+1]=num;s[x+1]=i;
    dfs(x+1,res-i*num,num+1);
  }
}

int main()
{
  scanf("%d",&n);
  fac[0]=1;
  for(int i=1;i<=n;i++)fac[i]=fac[i-1]*i%mod;
  dfs(0,n,1);
  printf("%d\n",ans*Pow(fac[n],mod-2)%mod); 
  return 0;
} 

猜你喜欢

转载自blog.csdn.net/wcy_1122/article/details/79349229