bzoj 4455: [Zjoi2016]小星星
链接
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(n−1)+ans(n−2)−ans(n−3)+ans(n−4).....
我们可以二进制枚举他的子集进行容斥复杂度$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; }