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;
}
posted @ 2020-09-18 10:00  Iscream-2001  阅读(325)  评论(0编辑  收藏  举报
/* */ /* */