luogu 3258 [JLOI2014]松鼠的新家

陌路散爱 提交于 2019-11-28 15:58:15

题目

题目描述

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

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!