首页 > 代码库 > bzoj 3812 主旋律

bzoj 3812 主旋律

这题好难啊 为什么dp都这么难 计数问题  好像完全不会啊

这是wc2015 陈老师讲的题

我们可以先考虑把这个点集拆成多个强连通分量 如果强连通分量的数量大于2 那么这一个生成子图对答案是没有贡献的

对于两个强连通分量从 拆出来的那一个 向其他的连边 是不会把两个强连通分量缩在一起的

考虑容斥?? 复杂度貌似有点高 要枚举每一个强连通分量

极强无比的做法::

  1. f[S]表示点集S的生成子图强联通的方案数 
  2. g[S]表示点集S的生成子图G中,若G的所有联通块都强联通,则G对g[S]存在一个贡献 
  3. 如果G中有奇数个连通块,则对g[S]的贡献为+1,否则为-1 
  4. h[S]表示点集S的诱导子图中有多少条边 
  5.  
  6. f[S]=2^h[S]-Σ[T是S的非空子集]2^cnt*g[T] 
  7. 其中cnt=|{x->y|x∈S,y∈S-T}|  
  8. (注意此时的g[S]不包含整个S强联通的情况) 

lef 即S-T

h[i^j]+w[j] 即cnt

(f[i]+=mod-pows[h[i^j]+w[j]]*g[j]%mod)%=mod;

  for(int j=lef;j;(--j)&=lef)
    (g[i]+=mod-f[i^j]*g[j]%mod)%=mod;

核心代码以给出

可是我总觉得这一类dp题 再出来我好像还是不太会做 开始怀疑做dp题看题解的意义

 1 #include <bits/stdc++.h>
 2 #define breaks printf("!")
 3 #define ll long long
 4 #define mod 1000000007
 5 using namespace std;
 6 
 7 int n,m,num[1<<8];
 8 int cd[1<<15],rd[1<<15];
 9 ll f[1<<15],g[1<<15],h[1<<15],pows[16*16];
10 inline Counts(int x){return num[x>>8]+num[x&255];}
11 int main()
12 {
13     scanf("%d%d",&n,&m);
14     for(int i=1;i<(1<<8);i++)
15         num[i]=num[i>>1]+(i&1);
16     pows[0]=1;for(int i=1;i<=m;i++) pows[i]=(pows[i-1]<<1)%mod;
17     for(int i=1;i<=m;i++)
18     {
19         int x,y; scanf("%d%d",&x,&y);
20         cd[1<<(x-1)]|=1<<(y-1); rd[1<<(y-1)]|=1<<(x-1);
21     }
22     //for(int i=1;i<=m;i++) printf("%d ",num[i]); printf("\n");
23     for(int i=1;i<1<<n;i++)
24     {
25         int one=i&-i,lef=i^one;
26         h[i]=h[lef]+Counts(rd[one]&lef)+Counts(cd[one]&lef);
27         for(int j=lef;j;(--j)&=lef)
28             (g[i]+=mod-f[i^j]*g[j]%mod)%=mod;
29         static int w[1<<15];
30         f[i]=pows[h[i]];
31         //breaks;
32         for(int j=i;j;(--j)&=i)
33         {
34             if(j==i) w[j]=0;
35             else 
36             {
37                 int tmp=(i^j)&-(i^j);
38                 w[j]=w[j^tmp]-Counts((i^j)&rd[tmp])+Counts(j&cd[tmp]);
39             }
40             (f[i]+=mod-pows[h[i^j]+w[j]]*g[j]%mod)%=mod;
41         }
42         g[i]+=f[i]; g[i]%=mod;
43     }
44     printf("%lld\n",f[(1<<n)-1]);
45     return 0;
46 }

 

bzoj 3812 主旋律