您的位置:首页 > 其它

[Bzoj3631][JLOI2014]松鼠的新家 (树上前缀和)

2017-11-04 15:22 453 查看

3631: [JLOI2014]松鼠的新家

 

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 2350  Solved: 1212
[Submit][Status][Discuss]

Description

 

松鼠的新家是一棵树,前几天刚刚装修了新家,新家有n个房间,并且有n-1根树枝连接,每个房间都可以相互到达,且俩个房间之间的路线都是唯一的。天哪,他居然真的住在“树”上。松鼠想邀请小熊维尼前来参观,并且还指定一份参观指南,他希望维尼能够按照他的指南顺序,先去a1,再去a2,……,最后到an,去参观新家。 可是这样会导致维尼重复走很多房间,懒惰的维尼不听地推辞。可是松鼠告诉他,每走到一个房间,他就可以从房间拿一块糖果吃。维尼是个馋家伙,立马就答应了。 现在松鼠希望知道为了保证维尼有糖果吃,他需要在每一个房间各放至少多少个糖果。因为松鼠参观指南上的最后一个房间an是餐厅,餐厅里他准备了丰盛的大餐,所以当维尼在参观的最后到达餐厅时就不需要再拿糖果吃了。

Input

 

第一行一个整数n,表示房间个数 第二行n个整数,依次描述a1-an 接下来n-1行,每行两个整数x,y,表示标号x和y的两个房间之间有树枝相连。

Output

 

一共n行,第i行输出标号为i的房间至少需要放多少个糖果,才能让维尼有糖果吃。

Sample Input

 

5
1 4 5 3 2
1 2
2 4
2 3
4 5

 



Sample Output

 

1
2
1
2
1

 


HINT

 

 

2<= n <=300000

 

分析:

今天做了noip2015 day2 t3,发现这道省选题竟然是它的简化版。。。。。。。。

道理一样求树上前缀和,以第一个访问的为根,求出dfs序(每个点的st和en)和lca。

对于每一个访问的点u,和前一个点pre在前缀和数组里 +1,他们的lca -2.

这样对于除了根节点以外的所有点,他们的起始位置到结尾位置的和就为那条边经过的次数。(这个用前缀和O(n)处理,每次求一个点只用sum[en] - sum[st - 1]就可以了)。

对于每条边出现次数x,两端的点答案各加x/2,如果为奇数深度更深的那个点答案再加1

根节点最后要加一,最后位置要减1,其实比noip那道题还简单。。。。。

AC代码:

 

# include <iostream>
# include <cstdio>
# include <cstring>
# include <cstdlib>
# include <algorithm>
using namespace std;
const int N = 3e5 + 12;
int head
,cnt,n,lg,maxn;
int fa
[31],dep
,vis
;
struct Edge{
int to,next;
}edge[N << 1];
void AddEdge(int u,int v){
Edge E = {v,head[u]};
edge[++cnt] = E;head[u] = cnt;
}
int st
,en
;
long long ans
,sum
;
void dfs(int u,int pre){
st[u] = ++cnt;
for(int i = head[u];i;i = edge[i].next){
int v = edge[i].to;
if(v == pre)continue;
fa[v][0] = u;
dep[v] = dep[u] + 1;
dfs(v,u);
}
maxn = max(maxn,dep[u]);
en[u] = cnt;
}
int lca(int x,int y){
if(dep[x] < dep[y])swap(x,y);
for(int i = lg;i >= 0;i--){
if(dep[x] - (1 << i) >= dep[y])x = fa[x][i];
}
for(int i = lg;i >= 0;i--){
if((dep[x] - (1 << i)) && fa[x][i] != fa[y][i]){
x = fa[x][i];
y = fa[y][i];
}
}
if(x != y)x = fa[x][0];
return x;
}
int main(){
scanf("%d",&n);
int x,y,root;
for(int i = 1;i <= n;i++){
scanf("%d",&vis[i]);
}
root = vis[1];
for(int i = 1;i < n;i++){
scanf("%d %d",&x,&y);
AddEdge(x,y);
AddEdge(y,x);
}
cnt = 0;
dep[root] = maxn = 1;
dfs(root,-1);
int pre = root;
for(lg = 0;(1 << lg) <= maxn;lg++);lg--;
for(int j = 1;j <= lg;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++){
sum[st[vis[i]]]++;sum[st[pre]]++;
sum[st[lca(vis[i],pre)]] -= 2;
pre = vis[i];
}
for(int i = 1;i <= n;i++){
sum[i] += sum[i - 1];
}
long long z;
for(int i = 1;i <= n;i++){
z = sum[en[i]] - sum[st[i] - 1];
if(z & 1LL){
ans[i]++;
}
ans[i] += z / 2LL;ans[fa[i][0]] += z / 2LL;
}
ans[pre]--;ans[root]++;
for(int i = 1;i <= n;i++){
printf("%lld\n",ans[i]);
}
}

 

 

 

 

 

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: