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;
}