题目大意:给定一棵无根树和一个序列,在这个序列上依次遍历,求每个点的访问次数
题解:裸的树上差分,但是根据题意,前一次的路径的结尾就是下一次路径的开头,相邻两次走的路径会有一个点重复计算,当然第一次不会重复,所以把差分数组cha[a[i]]−−,2≤i≤n
我的收获:……
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int M=300005;
const int L=20;
int n,t,head[M],a[M];
int cha[M],fa[M][L+1],dep[M];
struct edge{int to,nex;}e[M*2];
void add(int u,int v){e[t].to=v,e[t].nex=head[u];head[u]=t++;}
void dfs(int x,int F)
{
for(int i=1;i<=L;i++) fa[x][i]=fa[fa[x][i-1]][i-1];
for(int i=head[x];i!=-1;i=e[i].nex){
int v=e[i].to;
if(v==F) continue;
fa[v][0]=x,dep[v]=dep[x]+1;
dfs(v,x);
}
}
int lca(int x,int y)
{
if(dep[x]<dep[y]) swap(x,y);
for(int i=0;i<=L;i++) if((dep[x]-dep[y])&(1<<i)) x=fa[x][i];
if(x==y) return x;
for(int i=L;i>=0;i--) if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
void solve(int x,int y)
{
int z=lca(x,y);
cha[x]++,cha[y]++;
cha[z]--,cha[fa[z][0]]--;
}
void pushdown(int x,int F)
{
for(int i=head[x];i!=-1;i=e[i].nex){
int v=e[i].to;
if(v==F) continue;
pushdown(v,x);cha[x]+=cha[v];
}
}
void work()
{
for(int i=1;i<n;i++) solve(a[i],a[i+1]);
pushdown(1,0);
for(int i=2;i<=n;i++) cha[a[i]]--;
for(int i=1;i<=n;i++) printf("%d\n",cha[i]);
}
void init()
{
cin>>n;
t=0;memset(head,-1,sizeof(head));
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
int x,y;
for(int i=1;i<n;i++) scanf("%d%d",&x,&y),add(x,y),add(y,x);
dfs(1,0);
}
int main()
{
init();
work();
return 0;
}