bzoj3631: [JLOI2014]松鼠的新家(树上差分)
作者:互联网
题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=3631
题目大意:给定含有n个顶点的树,给定走遍整棵树顺序的序列a[1],a[2],a[3]……a[n],表示要从a[1]走到a[2],再从a[2]走到a[3],直到走到a[n],经过的点的点权需要加1,最后输出每个点的点权。
解题思路:首先我们讨论对于走一次的情况,假设从s走到t,我们可以让ans[s]++,ans[t]++,ans[LCA(s,t)]--,并且让s和t的LCA的父亲也减1,最后上推,然后第2个节点到第n个节点多走了一次,就减去1就可以了。
代码:
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=300000+7; int n,tot,a[maxn],head[maxn],fa[maxn][25],depth[maxn],ans[maxn]; struct Edge{ int v,next; }edge[maxn*2]; void add(int u,int v){ edge[tot].v=v; edge[tot].next=head[u]; head[u]=tot++; } void dfs(int u,int pre){ depth[u]=depth[pre]+1; fa[u][0]=pre; for(int i=1;i<=20;i++) fa[u][i]=fa[fa[u][i-1]][i-1]; for(int i=head[u];i!=-1;i=edge[i].next){ int v=edge[i].v; if(v==pre) continue; dfs(v,u); } } int LCA(int u,int v){ if(depth[u]<depth[v]) swap(u,v); for(int i=20;i>=0;i--){ if(depth[u]-(1<<i)>=depth[v]) u=fa[u][i]; } if(u==v) return u; for(int i=20;i>=0;i--){ if(fa[u][i]!=fa[v][i]) u=fa[u][i],v=fa[v][i]; } return fa[u][0]; } void dfs1(int u,int pre){ for(int i=head[u];i!=-1;i=edge[i].next){ int v=edge[i].v; if(v==pre) continue; dfs1(v,u); ans[u]+=ans[v]; } } int main(){ scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]); memset(head,-1,sizeof(head)); for(int i=1;i<n;i++){ int u,v; scanf("%d%d",&u,&v); add(u,v); add(v,u); } depth[0]=-1; dfs(1,0); for(int i=1;i<n;i++){ ans[a[i]]++; ans[a[i+1]]++; int lca=LCA(a[i],a[i+1]); ans[fa[lca][0]]--; ans[lca]--; } dfs1(1,0); for(int i=2;i<=n;i++)ans[a[i]]--; for(int i=1;i<=n;i++) printf("%d\n",ans[i]); return 0; }
标签:pre,JLOI2014,int,差分,bzoj3631,fa,edge,maxn,ans 来源: https://www.cnblogs.com/zjl192628928/p/11277482.html