1. 程式人生 > >bzoj 4455: [Zjoi2016]小星星

bzoj 4455: [Zjoi2016]小星星

std void == .com max 容斥 blog edge while

鏈接

http://www.lydsy.com/JudgeOnline/problem.php?id=4455

dp+容斥題意大約是樹上的點滿足與圖上的點一一對應並且圖中兩兩有邊,樹中也兩兩有邊,求滿足條件的方案數

只保證在樹在圖中兩兩有邊,用dp[i][j]表示樹上i點被映射到圖中的j點,以i為根的子樹方案數,那麽方案數可以用dp在$O(n^3)$時間內處理出來

我們把1設為樹的根,那麽就可以得方案數$\sum\limits_{i=1}^n f(1,i) $

這時的方案數是有重復的,考慮容斥

答案就是ans(n)−ans(n1)+ans(n2)−ans(n3)+ans(n4).....

我們可以二進制枚舉他的子集進行容斥復雜度$O(2^n)$

總復雜度O(2^nn^3)

這道題就做完了

#include<cstdio>
#include<cstring>

const int maxn = 50;
const int MAXN=1e6+10;
inline char nc()
{
    static char buf[MAXN],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,MAXN,stdin),p1==p2)?EOF:*p1++;
}
inline 
int read() { char c=nc();int x=0; while(c<0||c>9)c=nc(); while(c>=0&&c<=9)x=x*10+c-0,c=nc(); return x; } struct node{ int v,next; }edge[maxn*4]; int n,m,num; int map[maxn*maxn][maxn*maxn],head[maxn]; inline void add_edge(int u,int v) { edge[
++num].v=v;edge[num].next=head[u];head[u]=num; } int node_num=0; int node[maxn];long long dp[maxn][maxn]; void dfs(int x,int fa) { for(int i=1;i<=n;++i)dp[x][node[i]]=1; for(int i=head[x];i;i=edge[i].next) { int v=edge[i].v; if(v==fa)continue; dfs(v,x); for(int j=1;j<=node_num;++j) { long long cnt=0; for(int k=1;k<=node_num;++k) if(map[node[j]][node[k]]) cnt+=dp[v][node[k]]; dp[x][node[j]]*=cnt; } } } int main() { n=read(),m=read(); long long ans=0; for(int a,b,i=1;i<=m;++i) { a=read(),b=read(); map[a][b]=map[b][a]=1; } for(int a,b,i=1;i<n;++i) { a=read(),b=read(); add_edge(a,b); add_edge(b,a); } for(int i=1;i<(1<<n);++i) { node_num=0; for(int j=1;j<=n;++j) { if((1<<j-1)&i)node[++node_num]=j; } dfs(1,1); long long tot=0; for(int j=1;j<=node_num;++j) tot+=dp[1][node[j]]; if((node_num&1)==(n&1))ans+=tot; else ans-=tot; } printf("%lld\n",ans); return 0; }

bzoj 4455: [Zjoi2016]小星星