【Bzoj3631】松鼠的新家
2017-02-11 13:58
176 查看
3631: [JLOI2014]松鼠的新家
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 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
51 4 5 3 2
1 2
2 4
2 3
4 5
Sample Output
12
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
都要减一。为什么呐......嘿嘿嘿,不告诉你。
------------------------------------------我是一条萌萌哒的分割线>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; }
话说松鼠和熊的故事中是不是还少了个...光头...??!
相关文章推荐
- BZOJ 3631 [JLOI2014]松鼠的新家
- bzoj 3631: [JLOI2014]松鼠的新家(LCA+树上差分)
- bzoj 3631: [JLOI2014]松鼠的新家
- BZOJ 3631 JLOI2014 松鼠的新家 树链剖分/LCA
- BZOJ 3631 [JLOI2014]松鼠的新家 | 树上差分
- BZOJ3631 [JLOI2014]松鼠的新家 【树上差分】
- 【BZOJ3631】松鼠的新家 树链剖分
- bzoj 3631: [JLOI2014]松鼠的新家
- [BZOJ] 3631: [JLOI2014]松鼠的新家
- BZOJ3631 [JLOI2014] 松鼠的新家
- BZOJ3631 [JLOI2014]松鼠的新家 【树上差分】
- 【BZOJ-3631】松鼠的新家 树形DP?+ 倍增LCA + 打标记
- 【树链剖分】【树状数组】【最近公共祖先】【块状树】bzoj3631 [JLOI2014]松鼠的新家
- BZOJ 3631 松鼠的新家
- bzoj 3631 松鼠的新家
- BZOJ 3631 [JLOI2014]松鼠的新家==树剖
- bzoj3631[JLOI2014 松鼠的新家 倍增lca+差分
- [BZOJ3631]JLOI2014松鼠的新家|树上差分
- bzoj 3631: [JLOI2014]松鼠的新家
- bzoj 3631 [JLOI2014]松鼠的新家