1. 程式人生 > >BZOJ3812 清華集訓2014 主旋律

BZOJ3812 清華集訓2014 主旋律

com ring 強聯通 using ios 主旋律 編號 dig arr

技術分享圖片

直接求出強聯通生成子圖的數量較難,不妨用所有生成子圖的數量減去非強聯通的。

非強聯通生成子圖在所點後滿足編號最小的點所在的強聯通分量不是全集。

由於$n$很小,我們可以考慮狀態壓縮。

對於點集$S$,我們欽定一個它的子集$K$入度數為$0$,希望除去$K$以外的$S$度數不為$0$

設欽定$K$的度數為$0$其他隨意的方案數為$H_{S,K}=2^{sum_S-sum_{\{S^K\}\rightarrow\{k\}}}$

設$G_S$表示$S$分為奇數個強聯通分量的方案數減去分為偶數個強聯通分量的方案數。

設$F_S$表示$S$的強聯通生成子圖數。

$G_S=-\sum\limits_{K\subset S}F_{S-K}\cdot G_K$

$F_S=2^{sum_S}-\sum\limits_{K\subset S}H_{S,K} G_K$

細節處理,對於每一個$S$,先計算$F_S$,最後再將$F_S$再加到$G_S$中去。

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#define LL long long
#define mod 1000000007
#define M 33000
#define N 20
using namespace std;
int read(){
	int nm=0,fh=1;char cw=getchar();
	for(;!isdigit(cw);cw=getchar()) if(cw==‘-‘) fh=-fh;
	for(;isdigit(cw);cw=getchar()) nm=nm*10+(cw-‘0‘);
	return nm*fh;
}
int n,m,sq[M],u,v;
int ind[M],otd[M],G[M],F[M],cnt[M],MAXN,sum[M],W[M];
int mul(int x,int y){return (LL)x*(LL)y%mod;}
int add(int x,int y){return (x+y)>=mod?x+y-mod:x+y;}
int mus(int x,int y){return (x-y)<0?x-y+mod:x-y;}
void init(int now,int sta){
	if(!now) return; init((now-1)&sta,sta);
	int dt=(now&-now);
	W[now]=add(W[now^dt],cnt[ind[dt]&sta]);
}
int main(){
	n=read(),m=read(),sq[0]=1,MAXN=(1<<n);
	for(int i=1;i<=m;i++){
	    sq[i]=add(sq[i-1],sq[i-1]),u=read()-1,v=read()-1;
	    ind[1<<v]|=(1<<u),otd[1<<u]|=(1<<v);
    }
    for(int i=1;i<MAXN;i++) cnt[i]=cnt[i>>1]+(i&1);
    for(int i=1;i<MAXN;i++){	
    	int ot=i-(i&-i),dt=(i&-i);
    	sum[i]=sum[ot]+cnt[ind[dt]&i]+cnt[otd[dt]&i]; 
		F[i]=sq[sum[i]],init(i,i);
    	for(int S=ot;S;S=((S-1)&ot)) G[i]=mus(G[i],mul(F[i^S],G[S]));
    	for(int S=i;S;S=((S-1)&i)) F[i]=mus(F[i],mul(sq[sum[i]-W[S]],G[S]));	
    	G[i]=add(G[i],F[i]);
	}
	printf("%d\n",F[MAXN-1]);
	return 0;
}

BZOJ3812 清華集訓2014 主旋律