bzoj3812 主旋律(图论+状压dp+容斥原理)

题目给出一个 n 个点,m 条边的有向图,要求求出删掉一些边以后,整个图强联通的方案数,其中 n≤15,m≤n(n−1)
好难啊qaq 题解传送门:portal
实现上也有一些技巧要注意qaq

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
#define N 20
#define mod 1000000007
inline char gc(){
    static char buf[1<<16],*S,*T;
    if(T==S){T=(S=buf)+fread(buf,1,1<<16,stdin);if(T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(ch<'0'||ch>'9'){if(ch=='-') f=-1;ch=gc();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=gc();
    return x*f;
}
int n,m,out[33000],in[33000],bin[220],bitcnt[33000],f[33000],g[33000],h[33000],w[33000];
//f[S]--点集为S的子图强连通的方案数
//g[S],点集为S的子图分成奇数个scc的方案数-分成偶数个scc的方案数 h[S],点集为S的导出子图的边数 w[S],T集合为S的点时U-T连向T的边数
inline void dec(int &x,int y){x-=y;if(x<0) x+=mod;}
inline void inc(int &x,int y){x+=y;if(x>=mod) x-=mod;}
int main(){
//  freopen("a.in","r",stdin);
    n=read();m=read();bin[0]=1;
    for(int i=1;i<=m;++i) bin[i]=bin[i-1]<<1,bin[i]%=mod;
    for(int S=1;S<bin[n];++S) bitcnt[S]=bitcnt[S^(S&-S)]+1;
    while(m--){
        int x=bin[read()-1],y=bin[read()-1];
        out[x]|=y;in[y]|=x;
    }for(int S=1;S<bin[n];++S){
        int x=S&-S,t=S^x;//固定x在S^ss集合中
        for(int ss=t;ss;ss=(ss-1)&t)
            dec(g[S],(ll)f[S^ss]*g[ss]%mod);
        h[S]=h[t]+bitcnt[out[x]&S]+bitcnt[in[x]&S];f[S]=bin[h[S]];
        for(int ss=S;ss;ss=(ss-1)&S){//枚举T集合
            int x=(S^ss)&-(S^ss);
            if(ss!=S) w[ss]=w[ss|x]-bitcnt[in[x]&(S^ss)]+bitcnt[out[x]&ss];
            else w[ss]=0;dec(f[S],(ll)bin[w[ss]+h[S^ss]]*g[ss]%mod);
        }inc(g[S],f[S]);
    }printf("%d\n",f[bin[n]-1]);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/icefox_zhx/article/details/80746504
今日推荐