【BZOJ】1478: Sgu282 Isomorphism -Polya计数&dfs

版权声明:侵删,转载请附带链接或评论 https://blog.csdn.net/corsica6/article/details/81811079

传送门:bzoj1478


题解

设点数为 n m 种颜色。很容易想到点的置换方式有 n ! 种,直接枚举判断同构复杂度太高显然不行。

p o l y a 定理,可以通过求每个边转置群 g 循环组个数 s s g ,总转置集合为 G ,答案即为 g G m s s g

复杂度还是没有降下来,但 c c o s i 学习到了一个巧妙的方法(超神奇!):

考虑利用点置换转化为边置换,且方案只与点转置中每个循环组大小有关,而与具体转置方法无关。

枚举点转置的循环组(有向边形成的环,下文均称其为环),观察性质可以分别得到环内以及环之间的边的转置的循环组个数。这里的转置均考虑为环上的旋转操作。

设环的大小为 v ,即循环组内点数。则环内的边循环组个数为 v 2 。设另一个环的大小为 v ,则这两个环之间连边的循环组个数为 g c d ( v , v ) (一共 v × v 条边,旋转 l c m ( v , v ) 次后回到初始状态,循环组大小均为 l c m ( v , v ) ,所以个数为 v × v l c m ( v , v ) = g c d ( v , v ) )。

所以循环组总个数:

s s = i = 1 k ( v i 2 + j = i + 1 k g c d ( v i , v j ) )

所以 d f s 枚举每个循环组大小(按递降顺序排列,保证每种组合只统计到一次),复杂度统计类似于codevs2549 自然数和分解 的答案, n = 53 时,会枚举 329931 次。

现在每一种组合的答案都可以计算出来,只需要考虑计算每种组合在所有转置中出现的次数:

n 个点随机放入 k ( 1 k n ) 个环内的方案数: n ! v 1 ! v 2 ! . . . v k ! ,每个环内放好第一个点以后可以任意排列,所以要乘上 ( v 1 1 ) ! ( v 2 1 ) ! . . . ( v k 1 ) !

但考虑 v 相同的环方案数算重了,假设 c n t i 表示值为 i 的环的个数,还需要除去 c n t 1 ! c n t 2 ! . . . c n t n ! ,这里设 0 ! = 1

所以每种组合出现次数为:

n ! v 1 v 2 . . . v k c n t 1 ! c n t 2 ! . . . c n t n !

所以每次枚举到一种组合,把答案可以加上:

n ! v 1 v 2 . . . v k c n t 1 ! c n t 2 ! . . . c n t n ! m s s


代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

int n,m,mod,fac[60],cnt[60],v[60];
int gcd[60][60],ans,bs;
ll ss;

inline int GCD(int x,int y){return y==0?x:GCD(y,x%y);}

inline int fp(int x,ll y)
{
    int re=1;
    for(;y;y>>=1,x=1ll*x*x%mod)
     if(y&1) re=1ll*re*x%mod;
    return re;
}

inline void work(int num)
{
    int i,j;bs=1,ss=0;
    memset(cnt,0,sizeof(cnt));
    for(i=1;i<=num;++i) cnt[v[i]]++;
    for(i=1;i<=n;++i) bs=1ll*bs*fac[cnt[i]]%mod;
    for(i=1;i<=num;++i) bs=1ll*bs*v[i]%mod;
    for(i=1;i<=num;++i){
        ss+=(v[i]>>1);
        for(j=i+1;j<=num;++j) ss+=gcd[v[i]][v[j]];
    } 
    ans=(ans+1ll*fac[n]*fp(bs,mod-2)%mod*fp(m,ss)%mod)%mod;
}

inline void dfs(int mxv,int num,int sum)
{
    if(sum==n) {work(num);return;}
    for(int i=1;i<=mxv && i+sum<=n;++i){
        v[num+1]=i;dfs(i,num+1,sum+i);
    }
}

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

p.s.
%%%
USBZPZOJ
litble

猜你喜欢

转载自blog.csdn.net/corsica6/article/details/81811079