主旋律[清华集训2014 Day1]

SOL:

 这是最难的一道题。我一脸蒙蔽。

 首先我们发现正面做这道题很难。那么我们求答案的补集。

 我们把图缩点后,若其为点数大于1的DAG,那么我们就认为其不合法。

 利用容斥原理,DAG图的特征是有至少一个入度为0的点并且这个图不止一个点(这里及以下所说的点都是指求强连通后的  点),就根据这个进行容斥。

 我们枚举每一个点集。我们发现其子集对答案的贡献是负的。那么我们枚举每个集合和其子集,每个元素有三种状态,0,1,或是由1枚举子集至0,那么复杂度就是O(3^n);

#include<bits/stdc++.h>
#define sight(c) ('0'<=c&&c<='9')
#define LL long long
#define mo 1000000007
#define L(x) ((x)&(-(x)))
#define SIZ (1<<16)+7
using namespace std;
inline void read(int &x){
    static char c;
    for (c=getchar();!sight(c);c=getchar());
    for (x=0;sight(c);c=getchar()) x=x*10+c-48;
}
inline void add(LL &x,LL y){
    x=x+y; if (x>=mo) x%=mo; else if (x<0) (x%=mo)+=mo;
}
int n,m,x,y,in[SIZ],out[SIZ],cnt[SIZ];
LL po[SIZ],num[SIZ],f[SIZ],g[SIZ],M[SIZ],siz,v,ed,sum;
int main (){
    read(n); read(m);
    while (m--) {
        read(x),read(y);
        out[1<<x-1]|=1<<y-1; in[1<<y-1]|=1<<x-1;
    }
    po[0]=1; 
     for (int i=1;i<SIZ;i++) 
      po[i]=(po[i-1]<<1)%mo;
    siz=1<<n; 
    for (int i=1;i<siz;i++) num[i]=num[i-L(i)]+1;
    f[0]=g[0]=1; M[0]=mo-1;
    for (int i=1;i<siz;i++) {
        if (num[i]==1) {
            f[i]=M[i]=g[i]=1; continue;
        }
        x=L(i),v=i-x,ed=0;
        for (int j=0;j<n;j++) if (i&(1<<j)) ed+=num[out[1<<j]&i];
        f[i]=g[i]=po[ed],M[i]=0;
        for (int j=(v-1)&v,now=j|x;~j;j=(j-1)&v,now=j|x)  {
            add(M[i],-f[now]*M[i-now]%mo);
            if (!j) break;
        }
        cnt[i]=0; sum=M[i]*po[cnt[i]]%mo;
        for (int j=(i-1)&i;j;j=(j-1)&i) {
            int La=L(i-j),L=j|La;
            cnt[j]=cnt[L]-num[out[La]&(i-L)]+num[in[La]&j];
            add(sum,M[j]*g[i-j]%mo*po[cnt[j]]%mo);
        }
      add(f[i],-sum);add(M[i],f[i]);
    }
    printf("%lld
",f[(1<<n)-1]); return 0;
    return 0;
}
原文地址:https://www.cnblogs.com/rrsb/p/8260489.html