bzoj3812&uoj37 主旋律

正着做不好做,于是我们考虑反着来,如何计算一个点集s的答案呢,一定是所有的方案减去不合法的方案,不合法的方案一定是缩完点后是一个DAG,那么就一定有度数为0的scc,于是我们枚举s的子集,就是说这些点构成的scc的度数为0,这里我们就需要容斥了,容斥的目的是算出s集组成不合法的DAG的方案数,因为我们没有办法确定这里有几个scc。于是我们提前处理出g[s]表示这里面的每种不同scc的方案的贡献是$-1^{num-1}$,然后它们和其余的点之间随便连边,其余的点之间也随便连边,然后g数组我们是枚举任意一个点,然后枚举它所在的scc,然后在通过f数组转移,f就是总方案减去所有子集度数为0时的方案。

妙妙啊。

 1 #include <cstdio>
 2 #define N 16
 3 #define mod 1000000007
 4 using namespace std;
 5 int n,m,to[1<<N],cnt[1<<N],bin[N*N],e[1<<N];
 6 int f[1<<N],g[1<<N];
 7 int calc(int S,int T){
 8     int ans=0;
 9     for(;S;S-=S&-S)
10         ans+=cnt[to[S&-S]&T];
11     return ans;
12 }
13 int main(){
14     scanf("%d%d",&n,&m);
15     bin[0]=1;
16     for(int i=1;i<=m;i++)
17         bin[i]=bin[i-1]*2%mod;
18     for(int i=1,u,v;i<=m;i++){
19         scanf("%d%d",&u,&v);
20         to[1<<u-1]|=1<<v-1;
21     }
22     for(int i=1;i<bin[n];i++)cnt[i]=cnt[i>>1]+(i&1);
23     for(int i=1;i<bin[n];i++)e[i]=calc(i,i);
24     for(int i=1;i<bin[n];i++){
25         int k=i&-i,s=i^k;
26         for(int j=(s-1)&s;j;j=(j-1)&s)
27             g[i]=(g[i]-1ll*g[i^j^k]*f[j|k]%mod+mod)%mod;
28         if(i^k)g[i]=(g[i]-g[i^k]+mod)%mod;
29         f[i]=bin[e[i]];
30         for(int j=i;j;j=(j-1)&i)
31             f[i]=(f[i]-1ll*g[j]*bin[e[i^j]+calc(j,i^j)]%mod+mod)%mod;
32         (g[i]+=f[i])%=mod;
33     }
34     printf("%d\n",f[bin[n]-1]);
35     return 0;
36 }
View Code

 

posted @ 2018-04-16 18:06 Ren_Ivan 阅读(...) 评论(...) 编辑 收藏