P6789 寒妖王

P6789 寒妖王

链接

P6789 寒妖王

题解

月赛的时候暴力都没打对,我是真的菜。。
现在想想Fading在月赛的时候就已经跟我说过正解了,但我当时没听懂。
首先n<=15,求最大生成环套树的期望,考虑状压。
对于每条边肯定是要分别讨论在所有情况中的贡献。把边按照边权从大到小排序,权值小的边对权值大的边是没有影响的。
从大到小放入边的时候,一条边E(u,v)有贡献的情况有两种。
1.两端点u,v在同一联通块且所在联通块是一颗树。
2.两端点u,v不在同一联通块,且两个块至少有一个是树。
(不是树的那个联通块有几个环不重要,因为在最后选边集的时候只会在那里选一个基环树)
于是可以DP了QAQ。。
f[i][S]表示前i条边与点集S有交的边形成极大的恰好包含S的联通块的方案数。
g[i][S]表示前i条边与点集S有交的边形成极大的敲好包含S的数的方案数。
加入某条边时,枚举包含边的两个端点的点集S,
(f_{i,S}) (=) (2 cdot f_{i-1,S} + sum_{u in A , v in (S-A)}{f_{i-1,A} cdot f_{i-1,S-A}})
(g_{i,S}) (=) (g_{i-1,S} + sum_{u in A , v in (S-A)}{g_{i-1,A} cdot g_{i-1,S-A}})
然后就能按照之前的两种情况统计答案了。
不过对于某个点集S,在前i条边中有些与点集没有交,这些边与当前要计算贡献的边是无关的,可以预处理有多少这样的边,然后乘上2的几次方就好。
总效率是(O(m3^{n}))

(Code)

#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int N=3e5+10;
const LL P=998244353;
int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
void print(LL x){
    if(x>9) print(x/10);
    putchar(x%10+'0');
}
LL qpow(LL x,LL y){
	LL re=1;
	while(y){
		if(y&1) re=re*x%P;
		y>>=1;x=x*x%P;
	}
	return re;
}
int n,m;
struct edge{int l,r;LL w;}e[70];
bool cmp(edge x,edge y){return x.w>y.w;}
LL g[(1<<15)+10],f[(1<<15)+10],sz[(1<<15)+10];
LL bin[100];
int main(){
	bin[0]=1;for(int i=1;i<=70;++i) bin[i]=(bin[i-1]+bin[i-1])%P;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;++i) 
		scanf("%d%d%lld",&e[i].l,&e[i].r,&e[i].w);
	sort(e+1,e+1+m,cmp);
	for(int i=0;i<n;++i){
		g[(1<<i)]=1;
		f[(1<<i)]=1;
	}
	int x,U,V,S,A,B;
	LL ans=0,re,now;
	for(int i=1;i<=m;++i){
		x=(1<<(e[i].l-1))+(1<<(e[i].r-1));
		//贡献
		V=(1<<n)-1-x;U=V;re=0;
		for(U=V;;U=(U-1)&V){
			now=f[U+x]-g[U+x];
			//S=U+x;
			for(A=U;;A=(A-1)&U){
				now+=(f[A+(1<<(e[i].l-1))]-g[A+(1<<(e[i].l-1))])*(f[U-A+(1<<(e[i].r-1))]-g[U-A+(1<<(e[i].r-1))])%P;
				if(A==0) break;
			}
			now=(now%P+P)%P;
			now=now*bin[sz[U+x]]%P;
			re+=now;
			if(U==0) break;
		}
		re=re*bin[m-i]%P;
		re=(bin[m-1]-re+P)%P;
		re=re*e[i].w%P;
		ans+=re;
		//转移 
		V=(1<<n)-1-x;U=V;
		for(U=V;;U=(U-1)&V){
			//S=U+x;
			sz[U]++;
			f[U+x]=(f[U+x]+f[U+x]);if(f[U+x]>=P) f[U+x]-=P;
			for(A=U;;A=(A-1)&U){
				g[U+x]=(g[U+x]+g[A+(1<<(e[i].l-1))]*g[U-A+(1<<(e[i].r-1))])%P;
				f[U+x]=(f[U+x]+f[A+(1<<(e[i].l-1))]*f[U-A+(1<<(e[i].r-1))])%P;
				if(A==0) break;
			}
			if(U==0) break;
		}
	}
	ans=(ans%P+P)%P;
	ans=ans*qpow(bin[m],P-2)%P;
	cout<<ans<<endl;
	return 0;
}
原文地址:https://www.cnblogs.com/Yuigahama/p/13689614.html