"蔚来杯"2022牛客暑期多校训练营6-C Forest
原题题面:https://ac.nowcoder.com/acm/contest/33191/C
题目大意
给定 n ( 1 ≤ n ≤ 16 ) n(1\le n\le 16) n(1≤n≤16)个点 m ( 1 ≤ m ≤ 100 ) m(1\le m\le 100) m(1≤m≤100)条正边权的无向简单图,求每个生成子图的最小生成森林的权值和,答案对 998244353 998244353 998244353取模。
定义点集 V V V,边集 E E E,的图 G G G的最小生成森林为:
- 最小生成森林的边集 S ⊆ E S\subseteq E S⊆E。
- 任意两个节点的连通性不变。
-
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,i−1]的边无法使 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} 2m−i。
对于边编号在 [ 1 , i − 1 ] [1,i-1] [1,i−1]的无法使 u i , v i u_i,v_i ui,vi连通的边的影响,不太好求,不妨考虑使总方案数 − [ 1 , i − 1 ] -[1,i-1] −[1,i−1]之间的边使 u i u_i ui与 v i v_i vi连通的方案数。
不妨设点的全集为
V
V
V,边编号在
[
1
,
i
−
1
]
[1,i-1]
[1,i−1]之间的若干边构成使
u
i
u_i
ui与
v
i
v_i
vi连通的连通块点集为
S
S
S,方案数为
f
i
−
1
(
S
)
f_{i-1}(S)
fi−1(S),端点均在点集
A
A
A中且边编号在
[
1
,
i
−
1
]
[1,i-1]
[1,i−1]内的边数量为
c
n
t
i
−
1
cnt_{i-1}
cnti−1。
可得转移式:
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)=cnti−1(A)+[ui∈A&vi∈A]
每条边分为三种情况:
- 端点都不在点集 S S S中,对答案的贡献显然为 2 c n t i − 1 ( V − S ) 2^{cnt_{i-1}(V-S)} 2cnti−1(V−S)。
- 有且只有一个端点在 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}有两种情况:
- u i ∈ S , v i ∈ S u_i\in S,v_i\in S ui∈S,vi∈S
- 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)={2fi−1(S)+∑T∈S,ui∈T,vi∈/Tfi−1(T)fi−1(S−T)fi−1(S)ui∈S,vi∈Sothers
边
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×2m−i(2i−1−S⊂V,ui∈S,vi∈S∑fi−1(S)2cnti−1(V−S))
代码实现
#include<bits/stdc++.h>
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;
}