“蔚来杯“2022牛客暑期多校训练营6-C Forest

"蔚来杯"2022牛客暑期多校训练营6-C Forest

原题题面:https://ac.nowcoder.com/acm/contest/33191/C

文章目录

  • "蔚来杯"2022牛客暑期多校训练营6-C Forest
    • 题目大意
    • 解题思路
    • 代码实现

题目大意

给定 n ( 1 ≤ n ≤ 16 ) n(1\le n\le 16) n(1n16)个点 m ( 1 ≤ m ≤ 100 ) m(1\le m\le 100) m(1m100)条正边权的无向简单图,求每个生成子图的最小生成森林的权值和,答案对 998244353 998244353 998244353取模。

定义点集 V V V,边集 E E E,的图 G G G的最小生成森林为:

  • 最小生成森林的边集 S ⊆ E S\subseteq E SE
  • 任意两个节点的连通性不变。
  • S S S为满足以上条件的最小权值。
    G G G的生成子图为具有点集 V V V和边集为 E E E的子集所构成的图。

解题思路

看到范围,枚举显然不行。不妨将问题转换为求每条边对于总权值的贡献之和。

枚举边 e i = { u i , v i , w i } e_i=\{u_i,v_i,w_i\} ei={ui,vi,wi},分别求每条边的贡献,什么时候会将 e i e_i ei放入最小生成森林?先将边按权值从大到小排序,若边编号在 [ 1 , i − 1 ] [1,i-1] [1,i1]的边无法使 u i , v i u_i,v_i ui,vi连通,则会加边 e i e_i ei,而边编号在 [ i + 1 , m ] [i+1,m] [i+1,m]的边的影响显然为 2 m − i 2^{m-i} 2mi

对于边编号在 [ 1 , i − 1 ] [1,i-1] [1,i1]的无法使 u i , v i u_i,v_i ui,vi连通的边的影响,不太好求,不妨考虑使总方案数 − [ 1 , i − 1 ] -[1,i-1] [1,i1]之间的边使 u i u_i ui v i v_i vi连通的方案数。

不妨设点的全集为 V V V,边编号在 [ 1 , i − 1 ] [1,i-1] [1,i1]之间的若干边构成使 u i u_i ui v i v_i vi连通的连通块点集为 S S S,方案数为 f i − 1 ( S ) f_{i-1}(S) fi1(S),端点均在点集 A A A中且边编号在 [ 1 , i − 1 ] [1,i-1] [1,i1]内的边数量为 c n t i − 1 cnt_{i-1} cnti1
可得转移式:
c n t i ( A ) = c n t i − 1 ( A ) + [ u i ∈ A & v i ∈ A ] cnt_i(A)=cnt_{i-1}(A)+[u_i\in A\&v_i\in A] cnti(A)=cnti1(A)+[uiA&viA]
每条边分为三种情况:

  • 端点都不在点集 S S S中,对答案的贡献显然为 2 c n t i − 1 ( V − S ) 2^{cnt_{i-1}(V-S)} 2cnti1(VS)
  • 有且只有一个端点在 S S S中,不可选,没有贡献。
  • 端点都在点集 S S S中。

对于新加入的边 e i = { u i , v i , w i } e_i=\{u_i,v_i,w_i\} ei={ui,vi,wi}有两种情况:

  1. u i ∈ S , v i ∈ S u_i\in S,v_i\in S uiS,viS
  2. The others

对于第二种情况,该边对 f i ( S ) f_i(S) fi(S)没有贡献;
对于第一种情况,对 f i ( S ) f_i(S) fi(S)的贡献为 ∗ 2 *2 2,若 u i , v i u_i,v_i ui,vi不在同一个连通快中,还要加上连接两部分的贡献。
则:
f i ( S ) = { 2 f i − 1 ( S ) + ∑ T ∈ S , u i ∈ T , v i ∉ T f i − 1 ( T ) f i − 1 ( S − T ) u i ∈ S , v i ∈ S f i − 1 ( S ) o t h e r s f_i(S)= \begin{cases} 2f_{i-1}(S)+\sum_{T\in S,u_i\in T,v_i\notin T}f_{i-1}(T)f_{i-1}(S-T)&u_i\in S,v_i\in S\\ f_{i-1}(S)&others \end{cases} fi(S)={2fi1(S)+TS,uiT,vi/Tfi1(T)fi1(ST)fi1(S)uiS,viSothers
e i e_i ei对于结果的贡献为:
A n s i = w i × 2 m − i ( 2 i − 1 − ∑ S ⊂ V , u i ∈ S , v i ∈ S f i − 1 ( S ) 2 c n t i − 1 ( V − S ) ) Ans_i=w_i\times 2^{m-i}(2^{i-1}-\sum_{S\subset V,u_i\in S,v_i\in S}f_{i-1}(S)2^{cnt_{i-1}(V-S)}) Ansi=wi×2mi(2i1SV,uiS,viSfi1(S)2cnti1(VS))

代码实现

#include
using namespace std;
const int N=20,mod=998244353;
struct node{
	int x,y,w;
	node(int x,int y,int w):x(x),y(y),w(w){};
};
template <class T> inline void read(T&x){
    char c,last=' ';
    while(!isdigit(c=getchar()))last=c;
    x=c^48;
    while(isdigit(c=getchar()))x=(x<<1)+(x<<3)+(c^48);
    if(last=='-')x=-x;
}
int n,f[1<<N],ans,cnt[1<<N],fac[101],p[1<<N],w,V;
vector<node>g;
int add(int x,int y){return x+y>=mod?x+y-mod:x+y;}
int dec(int x,int y){return x-y<0?x-y+mod:x-y;}
int mul(int x,int y){return (long long)x*y%mod;}
bool cmp(node a,node b){
	return a.w<b.w;
}
int main(){
	read(n);
	for(int i=1;i<=n;i++){
		for(int j=1;j<=n;j++){
			read(w);
			if(j>i&&w){
				g.push_back(node(i,j,w));
			}
		}
	}
	V=dec(1<<n,1);
	sort(g.begin(),g.end(),cmp);
	fac[0]=1;
    for(int i=0;i<n;i++)f[1<<i]=1;
	for(int i=1;i<=100;i++)fac[i]=mul(fac[i-1],2);
	for(int i=0;i<g.size();i++){
		int u=g[i].x,v=g[i].y,w=g[i].w;u--,v--;
		int sum=fac[i],t=V;
        t^=1<<u,t^=1<<v;
		for(int j=0;j<=V;j++)if(1<<u&j&&1<<v&j)sum=dec(sum,mul(f[j],fac[cnt[V^j]]));
        ans=add(ans,mul(w,mul(fac[g.size()-i-1],sum)));
		for(int j=0;j<=V;++j)if(j&1<<u&&j&1<<v)cnt[j]++;
		for(int j=t;~j;j=(j?(j-1)&t:-1))
		for(int k=j;~k;k=(k?(k-1)&j:-1))
            p[j|1<<u|1<<v]=add(p[j|1<<u|1<<v],mul(f[(j^k)|1<<u],f[k|1<<v]));
		for(int j=0;j<=V;j++){
            if(j&1<<u&&j&1<<v)f[j]=add(f[j],f[j]);
            f[j]=add(f[j],p[j]);p[j]=0;
        }
	}
	cout<<ans;
} 

你可能感兴趣的:(多校联赛,“蔚来杯“,算法,图论,c++,动态规划)