版权声明:本文为DyingShu原创文章,转载请注明出处哦。 https://blog.csdn.net/DyingShu/article/details/82084719
题解:很简单,就是求树上点的差分。把从a[i]到a[i+1]的路径上的点覆盖一次,记录总覆盖次数
点的差分和边的差分略有不同,不过大体思路都是差不多的,边的差分是在起点,终点tag+1,LCA处-2;而点的差分是起点,终点tag+1,LCA及其父亲tag-1。为什么?模拟一下就行了
然后求个子树和.jpg
注意这道题起点和终点是连在一起的,所以除了第一条之外的所有起点都要tag-1。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN = 300001;
int a[MAXN], tag[MAXN], Ans[MAXN];
int fir[MAXN], nxt[MAXN << 1], to[MAXN << 1], cnt;
int fa[MAXN][20], dep[MAXN];
inline int read(){
int k = 0, f = 1; char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') f = -1; ch = getchar();}
while(ch >= '0' && ch <= '9'){k = k*10 + ch - '0'; ch = getchar();}
return k * f;
}
inline void add_edge(int a, int b){
to[cnt] = b;
nxt[cnt] = fir[a];
fir[a] = cnt++;
}
void dfs1(int u, int f){
dep[u] = dep[f] + 1;
fa[u][0] = f;
for(int i = 1; (1 << i) <= dep[u]; i++){
fa[u][i] = fa[fa[u][i - 1]][i - 1];
}
for(int i = fir[u]; i != -1; i = nxt[i]){
int v = to[i];
if(v != f) dfs1(v, u);
}
}
int lca(int x, int y){
if(dep[x] < dep[y]) swap(x, y);
for(int i = 19; i >= 0; i--){
if(dep[y] + (1 << i) <= dep[x]){
x = fa[x][i];
}
}
if(x == y) return x;
for(int i = 19; i >= 0; i--){
if(fa[x][i] != fa[y][i]){
x = fa[x][i], y = fa[y][i];
}
}
return fa[x][0];
}
void dfs2(int u, int f){
Ans[u] = tag[u];
for(int i = fir[u]; i != -1; i = nxt[i]){
int v = to[i];
if(v == f) continue;
dfs2(v, u);
Ans[u] += Ans[v];
}
}
int main(){
memset(fir, -1, sizeof(fir));
int n = read();
for(int i = 1; i <= n; i++){
a[i] = read();
}
for(int i = 1; i < n; i++){
int x = read(), y = read();
add_edge(x, y), add_edge(y, x);
}
dep[0] = -1;
dfs1(1, 0);
for(int i = 1; i < n; i++){
int LCA = lca(a[i], a[i + 1]);
tag[a[i]]++, tag[a[i + 1]]++, tag[LCA]--, tag[fa[LCA][0]]--;
}
dfs2(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;
}