【ABC253Ex】We Love Forest 题解

图上计数

Statement

给定一张有 n 个点,没有边的图

给定 m 条可能存在的边,有 k 次机会,每次可以等概率地抽取一条边并加入,同一条边可以被加入多次,m 条边中可能存在重边

对于每一个 k=1,2,,n1 ,求出得到的图为森林的概率

n14,n1m500

Solution

思路来自 https://atcoder.jp/users/GidroOttenok3797

概率直接转方案数

看到 n14, 想到这应该是一个带 2n 复杂度的解法

考虑设 g[i][S] 表示 i 步过后,s 集合成为森林的方案数,

考虑转移,按照围绕一个基准点构造一个整体的思想,我们固定集合之中最小的点,dia 出一棵包含 lowbit(S) 的树来

g[i][S]=lowbit(S)TSg[i|T|+1][S/T]×f[T]

其中 f[T] 表示 T 集合成为一棵树的方案数(花费 |T|1 次机会),那么

f[S]={1,|S|=1f[T]×f[S/T]×h(S,T),otherwise

其中 h(S,T) 表示集合 S 和集合 T 的连边数量,容斥一下

h(S,T)=e(S|T)e(S)e(T)

其中 e(S) 表示 S 中的边的数量

这样的话,我们只需要 O(n2n) 计算 e , O(3n) 计算 f , O(n3n) 计算 g 即可

答案为 g[i][2n1]×i!mi

Code

#include<bits/stdc++.h>
#define ppc(x) __builtin_popcount(x)
#define lowbit(x) (-x&x)
using namespace std;
const int mod = 998244353;
const int N = 15;
const int M = 505;

char buf[1<<23],*p1=buf,*p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
int read(){
    int s=0,w=1; char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
    while(isdigit(ch))s=s*10+(ch^48),ch=getchar();
    return s*w;    
}
int ksm(int a,int b){
    int res=1;
    while(b){
        if(b&1)res=1ll*res*a%mod;
        a=1ll*a*a%mod,b>>=1;
    }
    return res;
}
void inc(int &a,int b){a=a>=mod-b?a-mod+b:a+b;}
void dec(int &a,int b){a=a>=b?a-b:a+mod-b;}
int add(int a,int b){return a>=mod-b?a-mod+b:a+b;}
int del(int a,int b){return a>=b?a-b:a+mod-b;}
 
int f[1<<N|5],e[1<<N|5],mp[N][N],g[N][1<<N|5],fac[N];
int n,m;

signed main(){
    n=read(),m=read();
    for(int i=fac[0]=1;i<=n;++i)
        fac[i]=1ll*fac[i-1]*i%mod;
    for(int i=1,u,v;i<=m;++i)
        u=read(),v=read(),
        mp[u][v]++,mp[v][u]++;
    
    for(int i=1;i<1<<n;++i){
        int j=log2(lowbit(i))+1,dlt=0;
        for(int k=1;k<=n;++k)
            if(i&(1<<k-1))inc(dlt,mp[j][k]);
        e[i]=add(e[i-lowbit(i)],dlt);
    }
    // for(int i=1;i<1<<n;++i)cout<<e[i]<<" ";puts("");

    f[0]=1;
    for(int i=1;i<1<<n;++i)
        if(ppc(i)==1)f[i]=1;
        else{
            for(int j=(i-1)&i;j;j=(j-1)&i)
                inc(f[i],1ll*f[j]*f[i^j]%mod*(e[i]-e[j]-e[i^j])%mod);
            f[i]=1ll*f[i]*ksm(2ll*(ppc(i)-1),mod-2)%mod;
        }
    // for(int i=1;i<1<<n;++i)cout<<f[i]<<" ";puts("");

    for(int i=0;i<n;++i)
        for(int j=0;j<1<<n;++j){
            if(i+1==ppc(j))g[i][j]=f[j];
            int must=lowbit(j);
            for(int k=j;k;k=(k-1)&j)
                if(k&must&&i>=ppc(k)-1)
                    inc(g[i][j],1ll*g[i-ppc(k)+1][j^k]*f[k]%mod);
        }
    
    for(int i=1;i<n;++i)
        printf("%d\n",1ll*g[i][(1<<n)-1]*fac[i]%mod*ksm(ksm(m,i),mod-2)%mod);
    return 0;
}

/*
3 2
1 2
2 3
*/
posted @   _Famiglistimo  阅读(100)  评论(2编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示