朴素容斥原理[ZJOI2016][bzoj4455]小星星
前言
我容斥方面很菜啊,總是一頭霧水,于是決心好好學容斥 (從水題刷起)
題意簡介
題面鏈接
題目大意
給出一個nnn個點,mmm條邊的無向無重邊、無自環的圖,再給出一棵nnn個點的樹
定義一種對應為:讓每個樹中的點uuu都對應一個圖中的點u′u'u′(圖中所有點都要被對應,也即對應的點必須不同)
問有多少對應方式使得對于樹中每一組有邊相連的點對u,vu,vu,v,圖中的對應點u′,v′u',v'u′,v′之間也有邊相連
數據范圍
n≤17,m≤n?(n?1)/2n\le17,m\le n*(n-1)/2n≤17,m≤n?(n?1)/2
題解
部分分
直接dp
f[i][j][S]f[i][j][S]f[i][j][S]
表示iii節點及其子樹都已經做完,iii對應的圖中節點是jjj,iii節點及其子樹中的點對應圖中的節點集合是SSS,此時的方案數
具體流程就是枚舉每個狀態
轉移的時候是枚舉子節點以及子節點對應的點,然后還要枚舉子集(Θ(3n)\Theta(3^n)Θ(3n))
總復雜度是Θ(3nn2)\Theta(3^nn^2)Θ(3nn2)
正解
考慮部分分的復雜度瓶頸在于枚舉子集,考慮容斥優化
我們發現,由于有個限制“對應的點必須不同”,所以并不好做
考慮我們現放寬條件,求出能任意對應的時候的答案,再把哪些對應的點有相同的方案刪去
打出容斥的式子:
∣A1∪A2∪...∪An∣=∑i=1n(?1)i?1∑∣T∣=i,T={x1...xi}∣Ax1∩Ax2∩...∩Axi∣|{A_1}\cup{A_2}\cup...\cup{A_n}|=\sum_{i=1}^n(-1)^{i-1}\sum_{|T|=i,T=\{x_1...x_i\}}|{A_{x_1}}\cap{A_{x_2}}\cap...\cap{A_{x_i}}|∣A1?∪A2?∪...∪An?∣=i=1∑n?(?1)i?1∣T∣=i,T={x1?...xi?}∑?∣Ax1??∩Ax2??∩...∩Axi??∣
非常經典
設AiA_iAi?表示未匹配iii的方案,設UUU為所有的方案
那么相當于我們要求的就是∣U∣?∣A1∪A2∪...∪An∣|U|-|{A_1}\cup{A_2}\cup...\cup{A_n}|∣U∣?∣A1?∪A2?∪...∪An?∣
代入一下就是Ans=∣U∣?∑i=1n(?1)i?1∑∣T∣=i,T={x1...xi}∣Ax1∩Ax2∩...∩Axi∣=∣U∣+∑i=1n(?1)i∑∣T∣=i,T={x1...xi}∣Ax1∩Ax2∩...∩Axi∣\begin{aligned} Ans&=|U|-\sum_{i=1}^n(-1)^{i-1}\sum_{|T|=i,T=\{x_1...x_i\}}|{A_{x_1}}\cap{A_{x_2}}\cap...\cap{A_{x_i}}|\\ &=|U|+\sum_{i=1}^n(-1)^i\sum_{|T|=i,T=\{x_1...x_i\}}|{A_{x_1}}\cap{A_{x_2}}\cap...\cap{A_{x_i}}| \end{aligned}Ans?=∣U∣?i=1∑n?(?1)i?1∣T∣=i,T={x1?...xi?}∑?∣Ax1??∩Ax2??∩...∩Axi??∣=∣U∣+i=1∑n?(?1)i∣T∣=i,T={x1?...xi?}∑?∣Ax1??∩Ax2??∩...∩Axi??∣?
∣Ax1∩Ax2∩...∩Axi∣|{A_{x_1}}\cap{A_{x_2}}\cap...\cap{A_{x_i}}|∣Ax1??∩Ax2??∩...∩Axi??∣的意義是匹配的點不包含x1???xix_1···x_ix1????xi?,做的時候可以枚舉TTT的補集。∣U∣|U|∣U∣的答案是空集補集的貢獻。全集補集為空集、貢獻為000,所以不用枚舉
做的時候枚舉所有的點對應的點的集合SSS,代表對應的點不能在SSS之外,然后就Θ(n3)\Theta(n^3)Θ(n3)的樹形dp即可,總復雜度Θ(2n?n3)\Theta(2^n*n^3)Θ(2n?n3)
復雜度分析數值比較大,但是容易發現后面的Θ(n3)\Theta(n^3)Θ(n3)是不滿的,復雜度分析在1e81e81e8多一點,幾乎不用卡常即可過
代碼
貼上ac代碼
#include<cstdio> #include<cctype> #include<algorithm> #include<vector> namespace fast_IO {const int IN_LEN=10000000,OUT_LEN=10000000;char ibuf[IN_LEN],obuf[OUT_LEN],*ih=ibuf+IN_LEN,*oh=obuf,*lastin=ibuf+IN_LEN,*lastout=obuf+OUT_LEN-1;inline char getchar_(){return (ih==lastin)&&(lastin=(ih=ibuf)+fread(ibuf,1,IN_LEN,stdin),ih==lastin)?EOF:*ih++;}inline void putchar_(const char x){if(oh==lastout)fwrite(obuf,1,oh-obuf,stdout),oh=obuf;*oh++=x;}inline void flush(){fwrite(obuf,1,oh-obuf,stdout);} } using namespace fast_IO; #define getchar() getchar_() #define putchar(x) putchar_((x)) #define rg register typedef long long LL; template <typename T> inline T max(const T a,const T b){return a>b?a:b;} template <typename T> inline T min(const T a,const T b){return a<b?a:b;} template <typename T> inline void mind(T&a,const T b){a=a<b?a:b;} template <typename T> inline void maxd(T&a,const T b){a=a>b?a:b;} template <typename T> inline T abs(const T a){return a>0?a:-a;} template <typename T> inline void swap(T&a,T&b){T c=a;a=b;b=c;} template <typename T> inline T gcd(const T a,const T b){if(!b)return a;return gcd(b,a%b);} template <typename T> inline T lcm(const T a,const T b){return a/gcd(a,b)*b;} template <typename T> inline T square(const T x){return x*x;}; template <typename T> inline void read(T&x) {char cu=getchar();x=0;bool fla=0;while(!isdigit(cu)){if(cu=='-')fla=1;cu=getchar();}while(isdigit(cu))x=x*10+cu-'0',cu=getchar();if(fla)x=-x; } template <typename T> inline void printe(const T x) {if(x>=10)printe(x/10);putchar(x%10+'0'); } template <typename T> inline void print(const T x) {if(x<0)putchar('-'),printe(-x);else printe(x); } const int maxn=19; int n,m,bit[maxn]; bool edge[maxn][maxn]; std::vector<int>E[maxn]; int stack[maxn],top; inline void get(int x) {top=0;for(rg int i=1;x;i++,x>>=1)if(x&1)stack[++top]=i; } LL f[maxn][maxn],ans; void dfs(const int u,const int fa) {for(rg int t=1;t<=top;t++)f[u][t]=1;for(std::vector<int>::iterator Pos=E[u].begin();Pos!=E[u].end();Pos++){const int v=*Pos;if(v==fa)continue;dfs(v,u);for(rg int t=1;t<=top;t++){LL val=0;for(rg int l=1;l<=top;l++)if(edge[stack[t]][stack[l]])val+=f[v][l];f[u][t]*=val;}} } int main() {bit[1]=1;for(rg int i=2;i<=18;i++)bit[i]=bit[i-1]<<1; read(n),read(m);for(rg int i=1;i<=m;i++){int u,v;read(u),read(v);edge[u][v]=edge[v][u]=1;}for(rg int i=1;i<n;i++){int u,v;read(u),read(v);E[u].push_back(v),E[v].push_back(u);}for(rg int i=1;i<bit[n+1];i++){get(i);dfs(1,0);LL val=0;for(rg int j=1;j<=top;j++)val+=f[1][j];if((top&1)==(n&1))ans+=val;else ans-=val;}print(ans);return flush(),0; }總結
這是容斥的經典模式,寫篇博客來記錄&紀念一下
總結
以上是生活随笔為你收集整理的朴素容斥原理[ZJOI2016][bzoj4455]小星星的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: nth_element
- 下一篇: 二项式反演[bzoj3622]已经没有什