【XSY3032】画画(Burnside引理,计数)

20 篇文章 0 订阅
11 篇文章 0 订阅

为了方便,我们肯定是先考虑有标号图的个数,再用 Burnside 引理去重,但是用 Burnside 引理时得先考虑清楚映射集合 X X X 是哪个集合 A A A 到哪个集合 B B B 的哪些映射,以及作用在 A A A 上的置换群 G G G 是什么。

首先考虑两张有标号图等价的定义:存在一个置换使得第一张图在置换作用后得到的图与第二张图完全相同,也就是说任意一条边 ( u , v ) (u,v) (u,v) 要么在两者中同时存在,要么在两者中同时不存在。

那么自然地就能理清映射关系: X X X 是由大小为 n × ( n − 1 ) 2 \dfrac{n\times (n-1)}{2} 2n×(n1) 的边集 A A A B = { 0 , 1 } B=\{0,1\} B={0,1} 的所有映射, 0 0 0 表示这条边不存在, 1 1 1 表示这条边存在。

但是作用在 A A A 上的置换群 G G G 又是什么呢?题目中的置换是针对点的,貌似不是针对边的。

但我们知道,一种点置换对应着一种边置换,不同的点置换对应着不同的边置换,而且所有的点置换对应的所有的边置换也构成一个置换群。

于是 G G G 就是所有的点置换对应的所有的边置换。

但事实上,由于所有的点置换和 G G G 构成双射关系,我们可以通过枚举所有的点置换代替枚举 G G G 中的边置换,并且用作用于图上的点置换代替作用于图上的边置换。

考虑 Burnside 引理:
1 ∣ G ∣ ∑ g ∈ G ∣ X g ∣ \frac{1}{|G|}\sum_{g\in G}|X^g| G1gGXg
由刚刚我们说的双射关系, ∣ G ∣ |G| G 显然为 n ! n! n!

然后我们枚举所有的点置换,可以将一个置换划分为若干个环,发现若由这些环的大小构成的可重集相同,那么两种点置换对答案的贡献是一样的,所以我们可以枚举这些可重集。

假设现在已经枚举了一个可重集,我们需要找到在当前情况下的不动点个数。(注意此时图上的点是有标号的)

我们先考虑环内的连边:若环的大小为 2 s 2s 2s,那么有 s − 1 s-1 s1 种连边方案不会影响点的奇偶性,有 1 1 1 种方案会把这个环上所有的点的奇偶性都改变一次;若环的大小为 2 s + 1 2s+1 2s+1,则有 s s s 种连边方案不会影响点的奇偶性。

再考虑环与环之间的连边:假设两个环 A , B A,B A,B 的大小分别为 a , b a,b a,b,那么共有 gcd ⁡ ( a , b ) \gcd(a,b) gcd(a,b) 种连边方案,每一种连边方案都会给 A A A 环中的每个点多连出 Δ A = lcm ⁡ ( a , b ) a = b gcd ⁡ ( a , b ) \Delta A=\frac{\operatorname{lcm}(a,b)}{a}=\frac{b}{\gcd(a,b)} ΔA=alcm(a,b)=gcd(a,b)b 条边,会给 B B B 环中的每个点多连出 Δ B = lcm ⁡ ( a , b ) b = a gcd ⁡ ( a , b ) \Delta B=\frac{\operatorname{lcm}(a,b)}{b}=\frac{a}{\gcd(a,b)} ΔB=blcm(a,b)=gcd(a,b)a 条边。根据 Δ A \Delta A ΔA Δ B \Delta B ΔB 的奇偶性有三种可能:两个环上的点都不改变奇偶性、某个环上的点改变奇偶性、两个环上的点都改变奇偶性。

发现无论怎么连边,环上的点改变奇偶性是一起改变的,所以我们可以把一个环缩为一个点。而对于环与环之间连边的最后一种可能,我们在两个环之间连一条边。

我们先把那些不会改变任何点的奇偶性的连边方案给统计上,现在问题变成了:给定一张图,点权最初全是 0 0 0,每个点 i i i c p i cp_i cpi 次机会使点权异或 1 1 1,每条边 i i i c e i ce_i cei 次机会使两个端点的点权同时异或 1 1 1,问最后点权仍全是 0 0 0 的方案数。

对每个连通块分开考虑,我们先给这个连通块(假设大小为 s s s)随便找一棵 dfs 树,然后给所有的树边留一次异或的机会(共留下 s − 1 s-1 s1 次),然后假设连通块内除这 s − 1 s-1 s1 次机会以外的其他所有的机会是否用都已经确定了,那么有合法方案当且仅当对于单点的异或次数之和为偶数,而且若有合法方案那么这 s − 1 s-1 s1 次机会的使用情况也已经确定了,所以合法方案是唯一的。

所以这里贡献的方案数为 2 max ⁡ ( ∑ c p − 1 , 0 ) + ( ∑ c e ) − ( s − 1 ) 2^{\max(\sum cp-1,0)+(\sum ce)-(s-1)} 2max(cp1,0)+(ce)(s1),其中 ∑ c p − 1 \sum cp-1 cp1 是因为前 ∑ c p − 1 \sum cp-1 cp1 次机会任意选,然后根据奇偶确定最后一次机会是否选。

#include<bits/stdc++.h>

#define N 55

using namespace std;

namespace modular
{
    const int mod=998244353;
    inline int add(int x,int y){return x+y>=mod?x+y-mod:x+y;}
    inline int dec(int x,int y){return x-y<0?x-y+mod:x-y;}
    inline int mul(int x,int y){return 1ll*x*y%mod;}
}using namespace modular;

inline int poww(int a,int b)
{
    int ans=1;
    while(b)
    {
        if(b&1) ans=mul(ans,a);
        a=mul(a,a);
        b>>=1;
    }
    return ans;
}

inline int read()
{
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-') f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        x=(x<<1)+(x<<3)+(ch^'0');
        ch=getchar();
    }
    return x*f;
}

int n,p2[N*N*2],fac[N],ifac[N],inv[N],gcd[N][N];
int cnt,a[N];
int fa[N],sp[N],se[N],size[N];
int ans;

int ggcd(int a,int b)
{
    return b?ggcd(b,a%b):a;
}

int find(int x)
{
    return x==fa[x]?x:(fa[x]=find(fa[x]));
}

int calc()
{
    int ans=1,tim=0;
    for(int i=1;i<=cnt;i++)
    {
        ans=mul(ans,inv[a[i]]);
        tim++;
        if(a[i]!=a[i+1]||i==cnt)
        {
            ans=mul(ans,ifac[tim]);
            tim=0;
        }
    }
    for(int i=1;i<=cnt;i++) fa[i]=i,sp[i]=se[i]=0,size[i]=1;
    for(int i=1;i<=cnt;i++)
    {
        ans=mul(ans,p2[(a[i]-1)>>1]);
        if(!(a[i]&1)) sp[i]++;
    }
    for(int i=1;i<=cnt;i++)
    {
        for(int j=i+1;j<=cnt;j++)
        {
            int g=gcd[a[i]][a[j]],A=a[j]/g,B=a[i]/g;
            if(!(A&1)&&!(B&1)) ans=mul(ans,p2[g]);
            else if((A&1)&&(B&1))
            {
                int x=find(i),y=find(j);
                se[x]+=g;
                if(x!=y)
                {
                    fa[y]=x;
                    sp[x]+=sp[y];
                    se[x]+=se[y];
                    size[x]+=size[y];
                }
            }
            else if(A&1) sp[find(i)]+=g;
            else sp[find(j)]+=g;
        }
    }
    for(int i=1;i<=cnt;i++)
        if(find(i)==i)
            ans=mul(ans,p2[max(sp[i]-1,0)+se[i]-(size[i]-1)]);
    return ans;
}

void dfs(int sum,int lst)
{
    if(sum==n)
    {
        ans=add(ans,calc());
        return;
    }
    for(int i=lst;i<=n-sum;i++)
    {
        a[++cnt]=i;
        dfs(sum+i,i);
        cnt--;
    }
}

int main()
{
    n=read();
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            gcd[i][j]=ggcd(i,j);
    p2[0]=1;
    for(int i=1;i<=n*n*2;i++) p2[i]=add(p2[i-1],p2[i-1]);
    fac[0]=1;
    for(int i=1;i<=n;i++) fac[i]=mul(fac[i-1],i);
    ifac[n]=poww(fac[n],mod-2);
    for(int i=n;i>=1;i--) ifac[i-1]=mul(ifac[i],i);
    for(int i=1;i<=n;i++) inv[i]=poww(i,mod-2);
    dfs(0,1);
    printf("%d\n",ans);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值