题目传送门
题目大意:
给出一个在树上移动的序列,求出每个点被经过的次数。
思考过程:
维护两个点之间的链的信息很明显我们需要树剖,但是树剖无法维护链上每个节点的信息,所以我们需要借助差分数组。
具体做法:
1.树剖
2.对于序列中相邻的两个点,像普通树剖求答案那样往上跳到LCA,过程中将两个节点的差分数组起点+1,终点-1,注意重复的情况,用tag数组来记录
代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn=3e5+1000;
struct stu
{
int to,next;
}road[maxn*2]; int first[maxn],cnt=0;
int dep[maxn],cnum[maxn],id[maxn],dfn[maxn],top[maxn],fa[maxn],hson[maxn],cf[maxn],sum[maxn],tag[maxn],a[maxn];
int n,nowtop;
void addedge(int x,int y)
{
road[++cnt].to=y;
road[cnt].next=first[x];
first[x]=cnt;
}
void dfs1(int now,int depth)
{
int maxx=0;
dep[now]=depth;
cnum[now]=1;
for(int i=first[now];i;i=road[i].next)
{
int to=road[i].to;
if(to==fa[now]) continue;
fa[to]=now;
dfs1(to,depth+1);
cnum[now]+=cnum[to];
if(cnum[to]>maxx)
{
maxx=cnum[to];
hson[now]=to;
}
}
}
void dfs2(int now,bool what)
{
if(what==1) nowtop=now;
top[now]=nowtop;
id[now]=++cnt;
dfn[cnt]=now;
if(!hson[now]) return;
dfs2(hson[now],0);
for(int i=first[now];i;i=road[i].next)
{
int to=road[i].to;
if(to==fa[now]||to==hson[now]) continue;
dfs2(to,1);
}
}
void calcu(int x,int y)
{
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]]) swap(x,y);
cf[id[top[x]]]++;cf[id[x]]--;
tag[x]++;
x=fa[top[x]];
}
if(dep[x]<dep[y]) swap(x,y);
cf[id[y]]++;cf[id[x]]--;
tag[x]++;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n-1;i++)
{
int x,y;
scanf("%d%d",&x,&y);
addedge(x,y);addedge(y,x);
}
dfs1(a[1],1);
cnt=0;
dfs2(a[1],1);
calcu(a[1],a[2]);
for(int i=2;i<=n-1;i++)
{
calcu(a[i],a[i+1]);
tag[a[i]]--;
}
tag[a[n]]--;
sum[1]=cf[1];
for(int i=2;i<=n;i++) sum[i]=sum[i-1]+cf[i];
for(int i=1;i<=n;i++) printf("%d\n",sum[id[i]]+tag[i]);
return 0;
}