题目
题目描述
松鼠的新家是一棵树,前几天刚刚装修了新家,新家有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 }