【Bzoj3631】松鼠的新家

24 篇文章 0 订阅
5 篇文章 0 订阅

3631: [JLOI2014]松鼠的新家

Time Limit: 10 Sec   Memory Limit: 128 MB
Submit: 1780   Solved: 865
[ 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


两种常见解法:树剖维护线段树,树上差分LCA。这里用的是倍增求LCA之后树上差分的方法,而且效率比一些神方法低,但是理解起来的话还是挺简单的。
a数组的话意思不用多说,但是注意事项的话,要以a[1]而不是1为根,下放标记也以a[1]开始。其他数组应该意思也很明显,主要注意到tmp数组。保存的也就是这个点被访问的次数,我们采用差分的思想,每次经过一条边,(如从u到v)我们让tmp[u]++,tmp[v]++,tmp[LCA(u,v)]--,tmp[grand[LCA(u,v)][0]]--。(最后要把tmp推上去)以一次添加为例想象一下,首先u到根的路径上tmp都+1,此时u到根间结点tmp都为1,之后v到根路径上tmp+1,此时u到LCA前一个,v到LCA前一个点的tmp都+1,而LCA到根的所有点都+2,然后从tmp[LCA]--,更新上去,此时u-v路上所有tmp都+1,已经达到目的。而多余的是什么部分呢,也就是LCA的上一个结点(grand[LCA][0])到根的这一段都多加了1,所以tmp[grand[LCA][0]]--,更新上去,也就完成了。
实际操作时也就不需要每次更新都推上去,只要把四个tmp维护好,最后Dfs走一边就更新完了。但是最后求答案时,a[2]~a[n]都要减一。为什么呐......嘿嘿嘿,不告诉你。


------------------------------------------我是一条萌萌哒的分割线>w<-------------------------------------------------


咳咳,看在你这么有诚意地翻下来的份上,就告诉你。因为每次的终点就是下一次的起点(结束...也正是新的开始)←不正常。所以这样的话相当于后面的每个房间都多放了一颗,最后一个房间也是一样(所以题目说不用放糖),把它当作多算了一遍减去就好。这题的样例还算良心的,可以对着图推一推吧...看懂意思比较重要。树剖的话...hhhhhhh学会了以后再说吧。

#include <cstdio>
#include <algorithm>

using namespace std;

const int maxx = 300000 + 100;

int head[maxx],next[maxx<<1],to[maxx<<1];
int grand[maxx][20+2],depth[maxx],tmp[maxx],a[maxx];
bool done[maxx];
int n,m,root,x,y,num,size,Ans;

inline int read(){
    int x = 0,f = 1;char c = getchar();
    while(c>'9'||c<'0') {if(c == '-') f = -1;c = getchar();}
    while(c>='0'&&c<='9') {x = x*10+c-'0'; c = getchar();}
    return x*f;
}

void Add(int x,int y){
    to[++num] = y;
    next[num] = head[x];
    head[x] = num;
}

void Dfs(int x){
    done[x] = true;
    for(int i=1;i<=20;i++){
        if(depth[x] < (1<<i)) break;
        grand[x][i] = grand[grand[x][i-1]][i-1];
    }
    for(int i=head[x];i;i=next[i]){
        int now = to[i];
	    if(done[now]) continue;
    	grand[now][0] = x;
	    depth[now] = depth[x] + 1;
    	Dfs(now);
    }
}

int Lca(int x,int y){
    if(depth[x] > depth[y]) x^=y^=x^=y;
    int d = depth[y] - depth[x];
    for(int i=0;i<=20;i++)
	if((1<<i) & d)
	    y = grand[y][i];
    for(int i=20;i>=0;i--)
	if(grand[x][i] != grand[y][i])
	    x = grand[x][i],y = grand[y][i];
    return x == y? x : grand[x][0];
}

void pushdown(int x)
{
    for(int i=head[x];i;i=next[i])
    {
        if (to[i]==grand[x][0]) continue;
        pushdown(to[i]);
        tmp[x]+=tmp[to[i]];
	}
}

int main(){
    n = read();
    for(int i=1;i<=n;i++)
		a[i] = read();
    for(int i=1;i<n;i++){
        x = read();y = read();
	    Add(x,y);Add(y,x);
    }
    Dfs(a[1]);
    for(int i=1;i<n;i++){
	    int u = a[i],v = a[i+1];
    	tmp[u]++;tmp[v]++;
		tmp[Lca(u,v)]--;
		tmp[grand[Lca(u,v)][0]]--;
    }
    pushdown(a[1]);
    for(int i=2;i<=n;i++)
		tmp[a[i]]--;
	for(int i=1;i<=n;i++)
		printf("%d\n",tmp[i]);
    return 0;
}


话说松鼠和熊的故事中是不是还少了个...光头...??!


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值