【BZOJ4455】小星星(動態規劃,容斥)
阿新 • • 發佈:2018-07-29
之間 lld algorithm std 還需要 tchar 一次 lin 還需 為根的子樹(假裝以\(1\)號點為根節點的有根樹),並且\(i\)在圖上對應的點是\(j\)的方案數。
每次暴力選擇一個和當前\(i\)匹配的點,然後再暴力找到這個點在圖中的所有兒子,並且用子樹進行轉移,這樣\(dp\)一次的復雜度是\(O(n\times n\times n)\),即樹上每個點都要做一次,要暴力枚舉和哪個點進行匹配,還需要暴力枚舉兒子是哪個點,當然這樣肯定不滿。
再加上暴力枚舉可以進行匹配的點集的枚舉,
所以總的時間復雜度是\(O(n^32^n)\)
【BZOJ4455】小星星(動態規劃,容斥)
題面
BZOJ
洛谷
Uoj
題解
題意說簡單點就是給定一張\(n\)個點的圖和一棵\(n\)個點的樹,現在要讓圖和樹之間的點一一對應,並且如果樹上存在一條邊,那麽圖上對應的點對之間也要存在邊。
我們直接求解顯然很麻煩,一一對應是一個很不好算的東西。
那麽我們先要求並不需要一一對應,隨意對應即可,最後再減掉不合法的方案,這樣就可以用容斥來解決。
怎麽容斥呢?無非是考慮沒有一一對應的關系,那麽我們先暴力枚舉一下哪些點在圖上可以和樹上的點進行對應,其他的點不能夠和樹上的點進行匹配。
那麽考慮\(dp\)計算方案數。
設\(f[i][j]\)表示當前以\(i\)
每次暴力選擇一個和當前\(i\)匹配的點,然後再暴力找到這個點在圖中的所有兒子,並且用子樹進行轉移,這樣\(dp\)一次的復雜度是\(O(n\times n\times n)\),即樹上每個點都要做一次,要暴力枚舉和哪個點進行匹配,還需要暴力枚舉兒子是哪個點,當然這樣肯定不滿。
再加上暴力枚舉可以進行匹配的點集的枚舉,
所以總的時間復雜度是\(O(n^32^n)\)
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<algorithm> using namespace std; #define ll long long #define MAX 20 inline int read() { int x=0;bool t=false;char ch=getchar(); while((ch<'0'||ch>'9')&&ch!='-')ch=getchar(); if(ch=='-')t=true,ch=getchar(); while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar(); return t?-x:x; } struct Line{int v,next;}e1[MAX*MAX<<1],e2[MAX<<1]; int h1[MAX],h2[MAX]; int cnt1=1,cnt2=1; int n,m,cnt,a[MAX]; inline void Add1(int u,int v){e1[cnt1]=(Line){v,h1[u]};h1[u]=cnt1++;} inline void Add2(int u,int v){e2[cnt2]=(Line){v,h2[u]};h2[u]=cnt2++;} ll f[MAX][MAX],ans=0,num; void dfs(int u,int ff) { for(int i=h2[u];i;i=e2[i].next) if(e2[i].v!=ff)dfs(e2[i].v,u); for(int i=1;i<=cnt;++i)//枚舉當前點得到匹配點 { f[u][a[i]]=1; for(int j=h2[u];j;j=e2[j].next) if(e2[j].v!=ff) { num=0; for(int k=h1[a[i]];k;k=e1[k].next)num+=f[e2[j].v][e1[k].v]; f[u][a[i]]*=num; if(!f[u][a[i]])break; } } } int main() { n=read();m=read(); for(int i=1,u,v;i<=m;++i)u=read(),v=read(),Add1(u,v),Add1(v,u); for(int i=2,u,v;i<=n;++i)u=read(),v=read(),Add2(u,v),Add2(v,u); for(int i=1;i<(1<<n);++i)//枚舉可以進行匹配的點集 { cnt=0;memset(f,0,sizeof(f)); for(int j=0;j<n;++j)if(i&(1<<j))a[++cnt]=j+1; dfs(1,0);num=0; for(int j=1;j<=cnt;++j)num+=f[1][a[j]]; if((n-cnt)&1)ans-=num;else ans+=num; } printf("%lld\n",ans); return 0; }
【BZOJ4455】小星星(動態規劃,容斥)