[容斥 状压DP] HDU4997. Biconnected

fS 表示点集 S 的答案, gS 表示点集 S 的连通图个数

那么 gS 可以通过枚举与编号最小的点联通的点集求出来

fS=gSTSgT×MT,ST MS,T 表示把点集 S 分成几个联通块后连到 T 上的方案数

#include 
#include 
#include 
#include 

using namespace std;

const int P=1e9+7,N=1<<12;

int t,n,m;
int e[15][15],f[N],size[N],edge[N],g[N],ug[N],h[N],mer[1<<10|5][1<<10|5],pw[1010];

int G(int S){
  if(~g[S]) return g[S];
  int ret=pw[edge[S]],lst=S&-S; S^=lst;
  for(int s=S;s;s=(s-1)&S)
    ret=(ret-1LL*G(S^s^lst)*pw[edge[s]])%P;
  return g[S^lst]=ret;
}

int Edge(int S,int T){
  return edge[S^T]-edge[S]-edge[T];
}

int M(int S,int T){
  if(!Edge(S,T)) return 0;
  if(~mer[S][T]) return mer[S][T];
  int ret=1LL*G(S)*Edge(S,T)%P,lst=S&-S; S^=lst;
  for(int s=S;s;s=(s-1)&S){
    ret=(ret+1LL*M(s,T)*G(S^s^lst)%P*Edge(S^s^lst,T))%P;
  }
  return mer[S^lst][T]=ret;
}

int F(int S){
  if(~f[S]) return f[S];
  int ret=G(S),lst=S&-S; S^=lst;
  for(int s=S;s;s=(s-1)&S)
    ret=(ret-1LL*F(S^s^lst)*M(s,S^s^lst))%P;
  return f[S^lst]=ret;
}

int main(){
  freopen("1.in","r",stdin);
  freopen("1.out","w",stdout);
  scanf("%d",&t);
  for(int i=1;i<(1<<10);i++) size[i]=size[i>>1]+(i&1);
  pw[0]=1; for(int i=1;i<=100;i++) pw[i]=2LL*pw[i-1]%P;
  while(t--){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
      for(int j=1;j<=n;j++) e[i][j]=1;
    for(int i=1,x,y;i<=m;i++)
      scanf("%d%d",&x,&y),e[x][y]=e[y][x]=0;
    memset(mer,-1,sizeof(mer));
    for(int i=0;i<(1<1;
    for(int S=1;S<(1<if(size[S]==1) continue;
      edge[S]=0;
      for(int i=1;i<=n;i++)
    for(int j=i+1;j<=n;j++)
      if(i!=j && (S>>(i-1)&1) && (S>>(j-1)&1))
        edge[S]+=e[i][j];
    }
    int cur=F(5);
    printf("%d\n",(P+F((1<1))%P);
  }
  return 0;
}

你可能感兴趣的:(DP,状压DP,容斥原理)