1 条题解

  • 0
    @ 2026-3-4 10:25:44

    好题。

    I. 染色

    枚举边复杂度显然过高,考虑枚举点。在点上,二分图有个很好的性质:有且仅有二分图能进行黑白染色。对应地,一个连通图的黑白染色方案一一对应一个极大可行边(不使其有奇环——即连接异色点的边)集合。

    遂考虑转化为对每个点集的每个染色方案计数。

    II. 连通?

    连通很难限制,假设暂时放弃连通。设 g(S)g(S) 表示点集 SS 所有染色方案对应的可行边方案数。可以枚举子集确定染色方案。若 TST \subseteq S 为黑点集,则对应的可行边方案数为 2cntScntTcntS\T\displaystyle 2^{cnt_S-cnt_T-cnt_{S \backslash T}},其中 cntUcnt_U 为点集 UU 内部连接的边数。

    但是,我们仍需求出连通的方案数。考虑钦点在某个顺序下“第一个”连通块 TT,只要让 TT 与剩下的点不连通,就可以减掉这些方案。这里可以设 TT 含有 SS 中标号最小的点。设 f(S)f(S) 为连通点集 SS 的相应方案数,则有 f(S)=g(S)tsf(T)g(S\T)f(S)=g(S)-\sum_{t\subset s}f(T)g(S\backslash T)

    于是本题就做完了。值得注意的是,黑白染色可以翻转,因此最后答案要除以 22

    const int N = 17;
    const ll P = 998244353;
    int n, m, s, S;
    inline int lowbit(int x) { return x & -x; }
    ll f[1 << N], g[1 << N], pw[N * N];
    int cnt[1 << N];
    
    int main() {
    	pw[0] = 1;
    	U (i, 1, N * N - 1) pw[i] = pw[i - 1] * 2 % P;
    
    	rd(n, m);
    	S = (1 << n);
    	U (i, 1, m) {
    		int u, v; rd(u, v);
    		--u; --v;
    		for (s = 0; s < S; ++s) if (((s >> u) & 1) && ((s >> v) & 1))
    			++cnt[s]; // 点集 s 的边数和
    	}
    	
    	for (s = 0; s < S; ++s) {
    		g[s] = 1; // 全白点
    		for (int t = s; t; (--t) &= s)
    			(g[s] += pw[cnt[s] - cnt[t] - cnt[s ^ t]]) %= P;
    	}
    	for (s = 0; s < S; ++s) {
    		f[s] = g[s];
    		for (int t = s; t; (--t) &= s) if (lowbit(s ^ t) > lowbit(t)) // 钦点 t 是含有标号最小的点的连通块
    			(f[s] -= f[t] * g[s ^ t] % P - P) %= P;
    	}
    	printf("%lld", f[S - 1] * ((P + 1) >> 1) % P);
    }
    
    // LUOGU_RID: 204624807
    #include <bits/stdc++.h>
    using namespace std;
    #define ll long long
    #define mod 998244353
    ll n,m,u,v,cnt[1<<17],f[1<<17],g[1<<17],pw[444];
    int main(){
    	cin>>n>>m;
    	pw[0]=1;
    	for(int i=1;i<=m;i++){
    		pw[i]=pw[i-1]*2%mod;
    		cin>>u>>v;
    		u--,v--;
    		for(int S=0;S<1<<n;S++)
    			if((S>>u&1)&&(S>>v&1))
    				cnt[S]++;
    	}
    	for(int S=0;S<1<<n;S++){
    		g[S]=1;
    		for(int T=S;T;T=S&T-1)
    			g[S]=(g[S]+pw[cnt[S]-cnt[T]-cnt[S^T]]);
    		f[S]=g[S];
    		int t=S&S-1;
    		for(int T=t;T;T=t&T-1)
    			f[S]=(f[S]-g[T]*f[S^T])%mod;
    	}
    	cout<<(f[(1<<n)-1]*(mod+1>>1)%mod+mod)%mod;
    	return 0;
    }
    
    • 1

    信息

    ID
    9342
    时间
    3000ms
    内存
    1024MiB
    难度
    10
    标签
    递交数
    6
    已通过
    3
    上传者