【题解】abc213_g Connectivity 2

abc213_g Connectivity 2

题意

给定一张 \(n\) 个点 \(m\) 条边的简单无向图 \(G\),从 \(G\) 中删除若干条边(可以是 \(0\))得到子图 \(H\),对于每个整数 \(k\),求出有多少张子图 \(H\) 满足 \(1\)\(k\) 联通。

答案对 \(998244353\) 取模。

\(n\le 17,m\le \frac{n(n-1)}{2}\)

题解

知识点:动态规划。

图论计数的经典状压 dp,值得一做。

\(f(S)\) 表示以 \(S\) 为顶点集的联通子图个数。

\(g(S)\) 表示以 \(S\) 为顶点集的所有子图个数。

则对于整数 \(k\),答案为:

\[\displaystyle \sum_{S\subseteq V,\{1,k\}\subseteq S} f(S)\times g(S\setminus T) \]

其中 \(V\)\(G\) 的顶点集,\(S\setminus T\) 表示属于 \(S\) 但不属于 \(T\) 的顶点集,这里默认的是 \(S\)\(S\setminus T\) 的点之间相互不连边,所以能做到不重不漏。

\(g(S)\) 是好求的,设连接顶点集 \(S\) 内部的边的数量为 \(E(S)\),则 \(g(S)=2^{E(S)}\),通过枚举每一条边贡献到了哪个集合,预处理可以做到 \(O(m\times 2^n)\)

初始时,\(f(\emptyset)=0\)

\(f(S)\) 使用正难则反,用以 \(S\) 为顶点集的所有子图个数减去不连通的个数,为了防止记重记漏,这里钦定一个只有一个点的顶点集 \(v=\operatorname{lowbit(S)}\),所枚举的 \(T\) 必须包含 \(v\),意义在于枚举 \(v\) 所在连通块的情况,有转移:

\[\displaystyle f(S)=g(S)-\sum_{T\subsetneq S,v\subseteq T} f(T)\times g(S\setminus T) \]

这里默认的也是 \(S\)\(S\setminus T\) 的点之间相互不连边。

枚举子集的总复杂度是 \(O(3^n)\) 的,最后计算答案的复杂度是 \(O(n\times 2^n)\) 的,所以总复杂度是 \(O(3^n + m\times 2^n + n\times 2^n)\)

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

#define rep(i,l,r) for(int i=(l);i<=(r);++i)
#define per(i,l,r) for(int i=(r);i>=(l);--i)
#define pr pair<int,int>
#define fi first
#define se second
#define pb push_back
#define all(x) (x).begin(),(x).end()
#define sz(x) (x).size()
#define bg(x) (x).begin()
#define ed(x) (x).end()

#define N 18
#define M (131072+5)
#define int long long

#define lb(x) (x&-x)

const int mod=998244353;
int n,m,f[M],g[M],ans[N],tot;

signed main(){
    // freopen(".in","r",stdin);
    // freopen(".out","w",stdout);
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);

    cin>>n>>m;

    tot=(1<<n)-1;

    rep(i,0,tot){
        g[i]=1;
    }

    rep(i,1,m){
        int u,v;
        cin>>u>>v;

        rep(s,0,tot){
            if((s>>(u-1)&1)&&(s>>(v-1)&1)){
                g[s]=g[s]*2%mod;
            }
        }
    }

    f[0]=0;
    
    rep(s,1,tot){
        int v=lb(s);

        f[s]=g[s];

        int t=(s-1)&s;
        while(t){
            if(!(t&v)){
                t=(t-1)&s;
                continue;
            }

            f[s]=(f[s]-f[t]*g[s^t]%mod+mod)%mod;
            
            t=(t-1)&s;
        }
    }

    rep(s,0,tot){
        rep(k,2,n){
            if((s>>(k-1)&1)&&(s&1)){
                ans[k]=(ans[k]+f[s]*g[tot-s]%mod)%mod;
            }
        }
    }

    rep(i,2,n){
        cout<<ans[i]<<"\n";
    }

    return 0;
}
posted @ 2025-07-17 11:10  Lucyna_Kushinada  阅读(12)  评论(0)    收藏  举报