[bzoj3812]主旋律

来源:互联网 发布:人工智能的细分领域 编辑:程序博客网 时间:2024/05/18 03:46

题目大意

问多少边的子集仍然是强联通的。

DP

设f[i]表示点集为i有多少边的子集强联通。
考虑补集转化,如果不是强联通,缩点后会形成一个DAG。用总的减去非法。
考虑容斥,枚举出度为0的点的点集j,那么如果包含奇数个强连通分量,则表示至少j个出度为0,容斥系数为负,否则容斥系数为正。
那么,我们可以设g[i]表示点集为i有多少边的子集形成奇数个互相之间没有边的强联通分量 ,h[i]表示点集为i有多少边的子集形成偶数个互相之间没有边的强联通分量。
g[i]=f[j]h[ij]
h[i]=f[j]g[ij]
为了不计重,j必须包含i中编号最小的点。
然后f也容易计算了。
f[i]=2i(h[j]g[j])2iji

#include<cstdio>#include<algorithm>#define fo(i,a,b) for(i=a;i<=b;i++)#define lowbit(x) (x&-x)using namespace std;typedef long long ll;const int maxn=15+10,mo=1000000007;int cnt[maxn][33000],mi[300],id[33000];int f[33000],g[33000],h[33000];int i,j,k,l,t,n,m,tot,top,num,ans;int main(){    scanf("%d%d",&n,&m);    fo(i,1,n) id[1<<(i-1)]=i;    mi[0]=1;    fo(i,1,m){        mi[i]=(ll)mi[i-1]*2%mo;        scanf("%d%d",&j,&k);        cnt[j][1<<(k-1)]++;    }    fo(i,1,n)        fo(j,1,(1<<n)-1)            cnt[i][j]=cnt[i][lowbit(j)]+cnt[i][j-lowbit(j)];    h[0]=1;    fo(i,1,(1<<n)-1){        if (i==lowbit(i)){            f[i]=g[i]=1;            h[i]=0;            continue;        }        tot=0;        /*fo(j,1,n)            if (i&(1<<(j-1))) tot+=cnt[j][i];*/        k=i;        while (k){            t=lowbit(k);            tot+=cnt[id[t]][i];            k-=t;        }        f[i]=mi[tot];        j=i;        while (1){            j=(j-1)&i;            if (!j) break;            if (j&lowbit(i)){                (g[i]+=(ll)f[j]*h[i-j]%mo);                if (g[i]>=mo) g[i]-=mo;                (h[i]+=(ll)f[j]*g[i-j]%mo);                if (h[i]>=mo) h[i]-=mo;            }            tot=0;            /*fo(k,1,n)                 if ((i-j)&(1<<(k-1))) tot+=cnt[k][i];*/            k=i-j;            while (k){                t=lowbit(k);                tot+=cnt[id[t]][i];                k-=t;            }            (f[i]-=(ll)g[j]*mi[tot]%mo)%=mo;            (f[i]+=mo)%=mo;            (f[i]+=(ll)h[j]*mi[tot]%mo);            if (f[i]>=mo) f[i]-=mo;        }        (f[i]+=h[i]);        if (f[i]>=mo) f[i]-=mo;        (f[i]-=g[i])%=mo;        (f[i]+=mo)%=mo;        (g[i]+=f[i]);        if (g[i]>=mo) g[i]-=mo;    }    ans=f[(1<<n)-1];    printf("%d\n",ans);}
0 0
原创粉丝点击