洛谷P6789 寒妖王 题解

题目链接:P6789 寒妖王

题目大意:定义一张图的价值是它的最大基环生成树,现在给你一张 \(n\) 个点 \(m\) 条边的无向图,每一条边有 \(\frac{1}{2}\) 的概率消失,问最后的图的期望价值,对 \(998244353\) 取模。

\(n\leq 15,m\leq 60\)


题解:先考虑一张图的最大基环生成树怎么做。

直接在 Kruskal 上改改就可以了。

所以我们的 DP 借鉴 Kruskal 的思路,每一条边在不在图中只与边权比它大的边有关,考虑一条边不在图中的两种情况。

  • 两个点在同一个联通块内但联通块不是树。
  • 两个点不在同一个联通块内但两个联通块都不是树。

所以我们只需要知道树的数量和连通图的数量减一下就可以得到答案,直接子集 DP 即可。

总时间复杂度: \(O(m 3^n)\)

代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int quick_power(int a,int b,int Mod){
	int ans=1;
	while(b){
		if(b&1){
			ans=1ll*ans*a%Mod;
		}
		a=1ll*a*a%Mod;
		b>>=1;
	}
	return ans;
}
const int Maxn=15;
const int Maxm=60;
const int Mod=998244353;
int n,m;
int limit;
struct Edge{
	int u,v,w;
	friend bool operator <(Edge a,Edge b){
		return a.w>b.w;
	}
}edge[Maxm+5];
int sum[1<<Maxn|5];
int bit[1<<Maxn|5];
int pow_2[Maxm+5],inv[Maxm+5];
int f[1<<Maxn|5],g[1<<Maxn|5];
int id[1<<Maxn|5];
void init(){
	pow_2[0]=1;
	for(int i=1;i<=Maxm;i++){
		pow_2[i]=(pow_2[i-1]<<1)%Mod;
	}
	inv[1]=1;
	for(int i=2;i<=Maxm;i++){
		inv[i]=1ll*(Mod-Mod/i)*inv[Mod%i]%Mod;
	}
	for(int i=1;i<(1<<Maxn);i++){
		for(int j=0;j<Maxn;j++){
			if((i>>j)&1){
				bit[i]=bit[i^(1<<j)]+1;
				id[i]=j;
				break;
			}
		}
	}
}
int solve(int x){
	memset(sum,0,sizeof sum);
	for(int i=1;i<x;i++){
		sum[(1<<edge[i].u)|(1<<edge[i].v)]++;
	}
	for(int i=0;i<n;i++){
		for(int j=0;j<=limit;j++){
			if((j>>i)&1){
				sum[j]+=sum[j^(1<<i)];
			}
		}
	}
	for(int i=1;i<=limit;i++){
		if(bit[i]==1){
			f[i]=g[i]=1;
			continue;
		}
		if(sum[i]<bit[i]-1){
			f[i]=g[i]=0;
			continue;
		}
		f[i]=0,g[i]=pow_2[sum[i]];
		int s=i^(1<<id[i]);
		for(int j=s;j;j=(j-1)&s){
			int tmp=sum[i]-sum[j]-sum[i^j];
			f[i]=(f[i]+1ll*tmp*f[j]%Mod*f[i^j])%Mod;
			g[i]=(g[i]-1ll*g[i^j]*pow_2[sum[j]]%Mod+Mod)%Mod;
		}
		f[i]=1ll*f[i]*inv[bit[i]-1]%Mod;
	}
	for(int i=0;i<(1<<n);i++){
		g[i]=(g[i]-f[i]+Mod)%Mod;
	}
	int ans=0;
	int u=edge[x].u,v=edge[x].v;
	for(int i=0;i<=limit;i++){
		if((!((i>>u)&1))||(!((i>>v)&1))){
			continue;
		}
		ans=(ans+1ll*g[i]*pow_2[sum[limit^i]])%Mod;
	}
	for(int A=1;A<=limit;A++){
		if(((A>>u)&1)||(!((A>>v)&1))){
			continue;
		}
		int T=limit^A^(1<<u);
		for(int B=T;B;B=(B-1)&T){
			ans=(ans+1ll*g[A]*g[B|(1<<u)]%Mod*pow_2[sum[limit^A^(B|(1<<u))]])%Mod;
		}
	}
	ans=1ll*ans*quick_power(pow_2[x],Mod-2,Mod)%Mod;
	ans=(inv[2]-ans+Mod)%Mod;
	return ans;
}
int main(){
	init();
	scanf("%d%d",&n,&m);
	limit=(1<<n)-1;
	for(int i=1;i<=m;i++){
		scanf("%d%d%d",&edge[i].u,&edge[i].v,&edge[i].w);
		edge[i].u--;
		edge[i].v--;
	}
	sort(edge+1,edge+1+m);
	int ans=0;
	for(int i=1;i<=m;i++){
		ans=(ans+1ll*solve(i)*edge[i].w)%Mod;
	}
	printf("%d\n",ans);
	return 0;
}
posted @ 2020-10-28 18:53  with_hope  阅读(358)  评论(0编辑  收藏  举报