hdoj5909 Tree Cutting(点分治+树上dp转序列dp)
题目链接:https://vjudge.net/problem/HDU-5909
题意:给一颗树,结点带权值v[i]<m。求异或和为k的子树个数(0<=k<m)。
思路:
首先点分治处理一颗树,跑一遍dfs得到该树的dfs序。然后我们用序列dp来做,用dp[i][j]表示必须包括重心,处理序列中第i个结点时异或和为j的子树个数,因为必须包括重心,所以能做到不重不漏。
现在讨论结点i已经决策完毕
如果选i+1:dp[i+1][j^v[id[i+1]]]+=dp[i][j]。(id[i+1]表示dfs序列中第i+1个结点的编号)。
如果不选i+1:dp[i+sz[id[i+1]]][j]+=dp[i][j]。(因为如果i+1不选的话,以i+1为根的子树里的结点都不能选,sz[i]表示结点i的子树的大小)。
最后ans[i]+=dp[t][i],t为该子树的大小。
AC代码:
#include<cstdio> #include<algorithm> #include<cstring> using namespace std; const int maxn=1e3+50; const int inf=0x3f3f3f3f; const int MOD=1e9+7; typedef long long LL; struct node{ int v,nex; }edge[maxn<<1]; int T,n,m,V[maxn],head[maxn],cnt; int sz[maxn],mson[maxn],Min,root,size,vis[maxn],id[maxn],t; LL ans[maxn],dp[maxn][maxn]; 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 dfs(int u,int fa){ sz[u]=1,id[++t]=u; for(int i=head[u];i;i=edge[i].nex){ int v=edge[i].v; if(v==fa||vis[v]) continue; dfs(v,u); sz[u]+=sz[v]; } } void solve(int u){ t=0; dfs(u,0); for(int i=1;i<=t;++i) for(int j=0;j<m;++j) dp[i][j]=0; dp[1][V[u]]=1; for(int i=1;i<=t-1;++i) for(int j=0;j<m;++j){ dp[i+1][j^V[id[i+1]]]=(dp[i+1][j^V[id[i+1]]]+dp[i][j])%MOD; dp[i+sz[id[i+1]]][j]=(dp[i+sz[id[i+1]]][j]+dp[i][j])%MOD; } for(int i=0;i<m;++i) ans[i]=(ans[i]+dp[t][i])%MOD; } 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]; getroot(v,0); fenzhi(root,size); } } int main(){ scanf("%d",&T); while(T--){ cnt=0; scanf("%d%d",&n,&m); for(int i=1;i<=n;++i) head[i]=vis[i]=0; for(int i=1;i<=n;++i) scanf("%d",&V[i]); for(int i=0;i<m;++i) ans[i]=0; 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); for(int i=0;i<m;++i){ printf("%lld",ans[i]); if(i!=m-1) printf(" "); } printf("\n"); } return 0; }
朋友们,无论这个世界变得怎样,只要能够为了当时纯粹的梦想和感动坚持努力下去,不管其它人怎么样,我们也能够保持自己的本色走下去。