zoukankan      html  css  js  c++  java
  • 洛谷P6789 寒妖王 题解

    题目链接:P6789 寒妖王

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

    (nleq 15,mleq 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
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    Noip2012 开车旅行
    「NOI2018」归程
    2019.10.30 队测(晚上)
    洛谷P1138 第k小整数
    洛谷P2870 [USACO07DEC]最佳牛线,黄金Best Cow Line, Gold
    Noip-pj2018游记
    洛谷P4994 终于结束的起点
    《退役的你》
    《膜你抄》
    洛谷P5087 数学
  • 原文地址:https://www.cnblogs.com/withhope/p/13858552.html
Copyright © 2011-2022 走看看