题目
题目描述
松鼠的新家是一棵树,前几天刚刚装修了新家,新家有n个房间,并且有n-1根树枝连接,每个房间都可以相互到达,且俩个房间之间的路线都是唯一的。天哪,他居然真的住在”树“上。
松鼠想邀请小熊维尼前来参观,并且还指定一份参观指南,他希望维尼能够按照他的指南顺序,先去a1,再去a2,......,最后到an,去参观新家。可是这样会导致维尼重复走很多房间,懒惰的维尼不停地推辞。可是松鼠告诉他,每走到一个房间,他就可以从房间拿一块糖果吃。
维尼是个馋家伙,立马就答应了。现在松鼠希望知道为了保证维尼有糖果吃,他需要在每一个房间各放至少多少个糖果。
因为松鼠参观指南上的最后一个房间an是餐厅,餐厅里他准备了丰盛的大餐,所以当维尼在参观的最后到达餐厅时就不需要再拿糖果吃了。
输入格式
第一行一个整数n,表示房间个数第二行n个整数,依次描述a1-an
接下来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<= n <=300000
分析
一道树上差分 + LCA的题
需要注意的是,每一次只对路径上的点candy+1,所以LCA要-1,而father[LCA]也要-1
最后输出时要-1是因为到达这个点时拿了一次糖,每次离开这个点的时候就不拿糖了
末尾的哪一个房间因为没有离开,本身不应该-1,现在输出时-1正好解决了题中“最后一个房间不拿糖”。
而第一个房间因为一开始就在,所以没有在“到达的时候”拿糖,输出时也不应该-1,所以输出前要++(注意是在最后一次DFS后++)
代码

1 /*********************
2 User:Mandy.H.Y
3 Language:c++
4 Problem:luogu3258
5 Algorithm:
6 *********************/
7
8 #include<bits/stdc++.h>
9
10 using namespace std;
11
12 const int maxn = 3e5 + 5;
13
14 int n,size;
15 int a[maxn],first[maxn];
16 int dep[maxn],father[maxn],top[maxn],cnt[maxn];
17 int candy[maxn];
18
19 struct Edge{
20 int v,nt;
21 }edge[maxn << 1];
22
23
24 template<class T>inline void read(T &x){
25 x = 0;bool flag = 0;char ch = getchar();
26 while(!isdigit(ch)) flag |= ch == '-',ch = getchar();
27 while(isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48),ch = getchar();
28 if(flag) x = -x;
29 }
30
31 template<class T>void putch(const T x){
32 if(x > 9) putch(x / 10);
33 putchar(x % 10 | 48);
34 }
35
36 template<class T>void put(const T x){
37 if(x < 0) putchar('-'),putch(-x);
38 else putch(x);
39 }
40
41 void file(){
42 freopen("3258.in","r",stdin);
43 freopen("3258.out","w",stdout);
44 }
45
46 void eadd(int u,int v){
47 edge[++size].v = v;
48 edge[size].nt = first[u];
49 first[u] = size;
50 }
51
52 void readdata(){
53 read(n);
54 for(int i = 1;i <= n; ++ i){
55 read(a[i]);
56 }
57 for(int i = 1;i < n; ++ i){
58 int u,v;
59 read(u);read(v);
60 eadd(u,v);
61 eadd(v,u);
62 }
63 }
64
65 void dfs(int u,int f,int d){
66 top[u] = u;father[u] = f;
67 dep[u] = d;cnt[u] = 1;
68 int son = 0,mcnt = 0;
69
70 for(int i = first[u];i;i = edge[i].nt){
71 int v = edge[i].v;
72 if(v == f) continue;
73 dfs(v,u,d + 1);
74 cnt[u] += cnt[v];
75 if(cnt[v] > mcnt){
76 mcnt = cnt[v];
77 son = v;
78 }
79 }
80 if(son) top[son] = u;
81 }
82
83 int find(int x){
84 return top[x] == x ? x : top[x] = find(top[x]);
85 }
86
87 int LCA(int x,int y){
88 if(find(x) == find(y)) return dep[x] < dep[y] ? x : y;
89 else return dep[top[x]] < dep[top[y]] ? LCA(x,father[top[y]]) : LCA(father[top[x]],y);
90 //又打错了
91 }
92
93 void dfs1(int u,int f){
94 for(int i = first[u];i;i = edge[i].nt){
95 int v = edge[i].v;
96 if(v == f) continue;
97 dfs1(v,u);
98 candy[u] += candy[v];
99 }
100 }//统计糖果
101
102 void work(){
103 dfs(1,0,1);
104 for(int i = 2;i <= n ; ++ i){
105 int anc = LCA(a[i],a[i-1]);
106 candy[a[i]]++;
107 candy[a[i-1]]++;
108 candy[anc]--;
109 candy[father[anc]]--;
110 //只对路径上的点有贡献,对LCA以上的点无贡献
111 }
112 dfs1(1,0);
113 candy[a[1]]++;
114 //末尾的哪一个房间因为没有离开,本身不应该-1,现在输出时-1正好解决了题中“最后一个房间不拿糖”。
115 //而第一个房间因为一开始就在,所以没有在“到达的时候”拿糖,输出时也不应该-1,所以输出前要++(注意是在最后一次DFS后++)
116 for(int i = 1;i <= n; ++ i){
117 put(candy[i]-1);putchar('\n');
118 // 最后输出时要-1是因为到达这个点时拿了一次糖,每次离开这个点的时候就不拿糖了
119 //但是算路径上的糖时要用,所以在输出时-1;
120 }
121 }
122
123 int main(){
124 // file();
125 readdata();
126 work();
127 return 0;
128 }
