(模板)hdoj5977 Garden of Eden(点分治)
题目链接:https://vjudge.net/problem/HDU-5977
题意:给一颗树,每个结点上有一个权值a[i],a[i]<=10,求有多少条路径满足这条路径上所有权值的结点都出现了。
思路:
首先利用二进制的思想,将a[i]转化为1<<(a[i]-1)。我们在子树中,计算出结点到重心的路径,用二进制表示,比如011表示该路径中权值3没有出现、权值1和2出现。因为k最大为10,那么我们在计算结果时把所有可能枚举一遍,也就1024,如果枚举的i和当前路径取或后=(1<<k)-1,那么该路径满足要求,加上即可。具体实现时用桶记录信息,mine[i]表示权值为i的路径的个数。
另外,题目规定不同的路径仅当起点终点均不同,所以(1,2)和(2,1)是两个合法解,我的处理是先getdis一遍得到桶的信息,处理子结点,现将该子结点的子树的信息清除掉,即change函数,dfs之后再恢复回来。这种处理很重要,所以把这题当作模板记录一下。
不得不说,写点分治时要非常细心,我总是半小时代码,1小时改bug,老是一些简单错误。
AC代码:
#include<cstdio> #include<algorithm> #include<cstring> using namespace std; typedef long long LL; const int maxn=50005; const int inf=0x3f3f3f3f; struct node{ int v,nex; }edge[maxn<<1]; int n,k,cnt,head[maxn],a[maxn],sz[maxn],mson[maxn],Min,size,root; int vis[maxn],flag; LL ans,mine[1050]; void adde(int u,int v){ edge[++cnt].v=v; edge[cnt].nex=head[u]; head[u]=cnt; } void getroot(int u,int fa){ sz[u]=1,mson[u]=0; for(int i=head[u];i;i=edge[i].nex){ int v=edge[i].v; if(vis[v]||v==fa) continue; getroot(v,u); sz[u]+=sz[v]; mson[u]=max(mson[u],sz[v]); } mson[u]=max(mson[u],size-sz[u]); if(mson[u]<Min) Min=mson[u],root=u; } void getdis(int u,int fa,int len){ ++mine[len]; for(int i=head[u];i;i=edge[i].nex){ int v=edge[i].v; if(vis[v]||v==fa) continue; getdis(v,u,len|a[v]); } } void change(int u,int fa,int len,int f){ mine[len]+=f; for(int i=head[u];i;i=edge[i].nex){ int v=edge[i].v; if(vis[v]||v==fa) continue; change(v,u,len|a[v],f); } } void dfs(int u,int fa,int len){ for(int i=0;i<(1<<k);++i){ if((i|len)!=flag) continue; if(!mine[i]) continue; ans+=mine[i]; } for(int i=head[u];i;i=edge[i].nex){ int v=edge[i].v; if(vis[v]||v==fa) continue; dfs(v,u,len|a[v]); } } void solve(int u){ getdis(u,0,a[u]); for(int i=0;i<(1<<k);++i){ if((i|a[u])!=flag) continue; if(!mine[i]) continue; ans+=mine[i]; } for(int i=head[u];i;i=edge[i].nex){ int v=edge[i].v; if(vis[v]) continue; change(v,u,a[u]|a[v],-1); dfs(v,u,a[u]|a[v]); change(v,u,a[u]|a[v],1); } memset(mine,0,sizeof(mine)); } void fenzhi(int u,int ssize){ vis[u]=1; solve(u); for(int i=head[u];i;i=edge[i].nex){ int v=edge[i].v; if(vis[v]) continue; Min=inf,root=0; size=sz[v]<sz[u]?sz[v]:ssize-sz[u]; getroot(v,0); fenzhi(root,size); } } int main(){ while(~scanf("%d%d",&n,&k)){ cnt=0; ans=0; flag=(1<<k)-1; for(int i=0;i<=n;++i) head[i]=vis[i]=0; memset(mine,0,sizeof(mine)); for(int i=1;i<=n;++i){ scanf("%d",&a[i]); a[i]=1<<(a[i]-1); } for(int i=1;i<n;++i){ int u,v; scanf("%d%d",&u,&v); adde(u,v); adde(v,u); } Min=inf,root=0,size=n; getroot(1,0); fenzhi(root,n); printf("%lld\n",ans); } return 0; }
朋友们,无论这个世界变得怎样,只要能够为了当时纯粹的梦想和感动坚持努力下去,不管其它人怎么样,我们也能够保持自己的本色走下去。