题目描述
松鼠的新家是一棵树,前几天刚刚装修了新家,新家有 (n) 个房间,并且有 (n-1) 根树枝连接,每个房间都可以相互到达,且俩个房间之间的路线都是唯一的。天哪,他居然真的住在“树”上。
松鼠想邀请小熊前来参观,并且还指定一份参观指南,他希望小熊能够按照他的指南顺序,先去 (a_1) ,再去 (a_2) ,(cdots),最后到 (a_n),去参观新家。可是这样会导致重复走很多房间,懒惰的维尼不停地推辞。可是松鼠告诉他,每走到一个房间,他就可以从房间拿一块糖果吃。
小熊是个馋家伙,立马就答应了。现在松鼠希望知道为了保证维尼有糖果吃,他需要在每一个房间各放至少多少个糖果。
因为松鼠参观指南上的最后一个房间 (a_n) 是餐厅,餐厅里他准备了丰盛的大餐,所以当维尼在参观的最后到达餐厅时就不需要再拿糖果吃了。
输入格式
第一行一个正整数 (n),表示房间个数第二行 (n) 个正整数,依次描述 (a_1, a_2,cdots,a_n)
接下来 (n-1) 行,每行两个正整数 (x,y),表示标号 (x) 和 (y) 的两个房间之间有树枝相连。
输出格式
一共 (n) 行,第 (i) 行输出标号为 (i) 的房间至少需要放多少个糖果,才能让小熊有糖果吃。
输入输出样例
输入 #1
5
1 4 5 3 2
1 2
2 4
2 3
4 5
输出 #1
1
2
1
2
1
说明/提示
对于全部的数据,(2 le n le 3 imes 10^52≤n≤3×10)
————————————————————————————————
首先考虑一下题意,输入的第二行给定了松鼠的访问节点的顺序,由于树上两点的路径唯一,所以全程路径已知。
对于这种关心树上路径的问题,树链剖分可做,但这东西我很久很久没写过了,所以用了一个思路比较简单的树上差分(基于点)的方法。
Link: 浅谈差分和树上差分算法的基本应用
LCA 是用倍增实现的,常数有点大,最慢的一个点在 Luogu 上跑了 927ms
具体细节请见代码的注释
代码如下
#include <bits/stdc++.h>
#define MAXN 300007
using namespace std;
int n,cf[MAXN],dep[MAXN];
int a[MAXN],ans[MAXN],fa[MAXN][20];
vector<int> G[MAXN];
inline int readint() { //快速读入
int w=0,X=0; char ch=0;
while (!isdigit(ch)) w|=ch=='-',ch=getchar();
while (isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
return w?-X:X;
}
void dfs(int u,int f) {
fa[u][0]=f,dep[u]=dep[f]+1;
for (int i=0;i<(int)G[u].size();i++)
if (G[u][i]!=f) dfs(G[u][i],u);
}
int lca(int a,int b) {
if (dep[a]>dep[b]) swap(a,b); //先让 b 在下面
//首先跳到同一深度
int c=dep[b]-dep[a];
for (int j=0;(1<<j)<=c;j++)
if ((1<<j)&c) b=fa[b][j];
//一起往上跳
if (a!=b) {
for (int j=19;j>=0;j--)
if (fa[a][j]!=fa[b][j]) a=fa[a][j],b=fa[b][j];
a=fa[a][0];
}
return a;
}
void stat(int u) { //用差分数组统计答案
ans[u]=cf[u];
for (int i=0;i<(int)G[u].size();i++) {
if(G[u][i]==fa[u][0]) continue;
stat(G[u][i]);
ans[u]+=ans[G[u][i]];
}
}
int main() {
memset(cf,0,sizeof(cf)); //差分数组
memset(fa,0,sizeof(fa)); //fa[i][j] 表示从 i 往上跳 2^j 次到达的节点
memset(dep,0,sizeof(dep)); //节点的深度
memset(ans,0,sizeof(ans)); //答案数组
n=readint();
for (int i=1;i<=n;i++) a[i]=readint();
for (int i=1;i<n;i++) {
int x=readint(),y=readint();
G[x].push_back(y),G[y].push_back(x);
}
//倍增求 LCA 的预处理
dfs(1,0);
for (int j=1;j<20;j++)
for (int i=1;i<=n;i++)
fa[i][j]=fa[fa[i][j-1]][j-1];
for (int i=1;i<n;i++) {
cf[a[i]]++,cf[a[i+1]]++;
cf[lca(a[i],a[i+1])]--,cf[fa[lca(a[i],a[i+1])][0]]--; //查封操作
}
stat(1);
for (int i=1;i<=n;i++) printf("%d
",a[1]==i?ans[i]:ans[i]-1);
return 0;
}