• ABC253EX (fzt子集计数+矩阵树定理)


    题意:给n个点m条边的无向图,定义f(k)为边集大小为k的森林数量。对k从1~n-1,求所有的f(k)。n<=14,m<=500

    Soltion:

    设f(S)表示,选定点集S能构成的生成树数量。

    设g(S,i)表示,选定点集S能且用了i条边所构成的森林的数量。

    那么答案就是求,对于所有的1~n-1,求g(U,i),U表示点的全集。

    对于f(S)的计算,用矩阵树定理可以在 O ( 2 n n 3 ) O(2^nn^3) O(2nn3)内计算。具体来说,就是枚举所有状态S,然后算。

    对于g(S)的计算,有转移方程
    g ( S , i ) = ∑ T ∈ S f ( T ) ∗ g ( S / T , i − ( ∣ T ∣ − 1 ) ) g(S,i)=\sum_{T∈S}f(T)*g(S/T,i-(|T|-1)) g(S,i)=TSf(T)g(S/T,i(T1)),枚举S的子集是树,同时枚举i,这里用递推好写一点,最里层枚举i,其次枚举子集。
    复杂度O(n3^n)。

    一个trick,就是算g(S,i)时候需要钦定一个点v不动,对于子集T如果T不包含v的话,则不算上T的贡献,不知道什么原理。。。不这样的话会算重。。。有大佬知道的话,麻烦留言区教教^ - ^。
    另一题ABC213G也有同样的trick。
    不过有另一种方程,具体是对于集合S,f(S)是点集S连通的方案,枚举其子集T,然后直接f(S) = ∑f(T)g(S/T)这样,数,钦定其连通的子图,然后剩下部分任意,这样保证了T和S/T不连通。因此不会算重。

    #define ll long long
    #define Maxn 305
    const int N=1e3+5;
    const int mod=998244353;
    ll powM(ll a,int t=mod-2){
        ll ans=1;
        while(t){
            if (t&1)ans=ans*a%mod;
            a=a*a%mod;t>>=1;
        }
        return ans;
    }
    int n,m;
    int a[N][N],mp[N][N];
    int f[1<<14],g[20][1<<14];
    ll det(ll a[][N],int n){
        ll ans=1;bool tr=0;
        for(int j=2;j<=n;j++){
            for(int i=j;i<=n;i++)
                 if (a[i][j]){
                    if (i!=j){
                        swap(a[i],a[j]);
                        tr=!tr;
                    }
                    break;
                    }
                if (a[j][j]==0)return 0;
        ans=ans*a[j][j]%mod;
        ll t=powM(a[j][j]);
        for (int k=j;k<=n;k++)a[j][k]=t*a[j][k]%mod;
        for (int i=j+1;i<=n;i++){
          t=mod-a[i][j];
          for (int k=j;k<=n;k++)
            a[i][k]=(a[i][k]+t*a[j][k])%mod;
        }
        }
        return tr?(mod-ans)%mod : ans;
    }
    bool pd(int i,int j){
        return 0<=i&&i<n&&0<=j&&j<m;
    }
    int fac[N],ni_f[N];
    ll qsm(int a,int b){
        ll ans=1,tmp=a;
        while( b ) {
            if( b&1 ) ans = ans * tmp%mod;
            tmp = tmp * tmp%mod;
            b>>=1;
        }
        return ans;
    }
    void ini(){
        int maxn = 1e3;
        fac[0]=1;
        _for(i,1,maxn) fac[i] =  (fac[i-1] * i)%mod;
        ni_f[maxn] = qsm(fac[maxn],mod-2);
        _rep(i,maxn-1,0) ni_f[i] = ni_f[i+1] * (i+1)%mod;
    }
    signed main(){
    #ifndef ONLINE_JUDGE
        freopen("in.txt", "r", stdin);
    #endif  
        ini();
        cin>>n>>m;
        _for(i,1,m){
            int u,v;cin>>u>>v;
            if( u > v ) swap(u,v);
            mp[v][u]++;
        }
        for(int S = 1 ;S<(1<<n) ;S++){
            _for(i,1,n) _for(j,1,n) a[i][j] = 0;
            int siz = 0;
            int id[n+1];
            for(int j=1 ;j<=n ;j++){
                if( S>>(j-1)&1 ){
                    id[j] = ++siz;
                    _for(k,1,j-1){
                        if( mp[j][k] && (S>>(k-1))&1 ){
                            int u = id[j];
                            int v = id[k];
                            int w = mp[j][k];
                            a[u][u] = (a[u][u] + w)%mod;
                            a[v][v] = (a[v][v] + w)%mod;
                            a[u][v] = (a[u][v] + mod - w)%mod;
                            a[v][u] = (a[v][u] + mod - w)%mod;
                        }
                    }
                }
            }
            f[S] = det(a,siz);
        }
        g[0][0] = 1;
        for (int i = 1; i < 1 << n; i++)
            for (int sub = i; sub > 0; sub = (sub - 1) & i)
                for (int k = 0; k < n;k++){
                    if( ~sub&(i&(-i)) ) continue;
                    int j = k + __builtin_popcount(sub) - 1;
                    if (j < n){
                        g[j][i] = (g[j][i] + f[sub] * g[k][sub ^ i] % mod) % mod;
                    }
                }
        int all = (1 << n) - 1;
        for (int i = 1; i < n; i++){
            int ans = g[i][all] * fac[i] %  mod * powM(powM(m, i)) % mod;
            cout<<ans<<endl;
        }
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
  • 相关阅读:
    LCD1602
    DRF JWT认证(二)
    年薪高达115万元,Rust成2021年最赚钱的编程语言
    深化校企合作 搭建技术技能人才成长“立交桥”
    设计原则之【依赖反转原则】
    2023小米秋招真题-手机流畅运行的秘密
    【Mysql】第4篇--多表查询和事务
    vector的使用
    如何防止企业代码被抄袭?源代码加密软件来救援!
    阶段总结之BBS
  • 原文地址:https://blog.csdn.net/m0_53688600/article/details/126889784