[BAOJ3631]松鼠的新家

松鼠的新家

题目描述

松鼠的新家是一棵树,前几天刚刚装修了新家,新家有n个房间,并且有n-1根树枝连接,每个房间都可以相互到达,且俩个房间之间的路线都是唯一的。天哪,他居然真的住在“树”上。松鼠想邀请小熊维尼前来参观,并且还指定一份参观指南,他希望维尼能够按照他的指南顺序,先去a1,再去a2,……,最后到an,去参观新家。

可是这样会导致维尼重复走很多房间,懒惰的维尼不听地推辞。可是松鼠告诉他,每走到一个房间,他就可以从房间拿一块糖果吃。维尼是个馋家伙,立马就答应了。

现在松鼠希望知道为了保证维尼有糖果吃,他需要在每一个房间各放至少多少个糖果。因为松鼠参观指南上的最后一个房间an是餐厅,餐厅里他准备了丰盛的大餐,所以当维尼在参观的最后到达餐厅时就不需要再拿糖果吃了。

输入

第一行一个整数n,表示房间个数

第二行n个整数,依次描述a1-an

接下来n-1行,每行两个整数x,y,表示标号x和y的两个房间之间有树枝相连。

输出

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

样例输入

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

样例输出

1
2
1
2
1

提示

2<= n <=300000

据说是个树链剖分的模板题,可惜,自己不行,字符串都没打完,就没看过树链剖分,就只能靠学过的东西了。很显然这就是个LCA,找到LCA,把两个点到LCA路径上的点都加一就对了,不过这么说LCA会加两次要减一,但是很显然,就算用树上倍增找到LCA,沿路径跳父亲加一也会超时,然后就出现了一个不算特别神奇的神奇的优化,差分思想。

这玩意在学树状数组的时候就学过,当时没整明白,还各种博客的看了好久,才懂了原理,然而并没有什么卵用,换个地方就废了,自己压根就想不到,学过的没办法灵活的根据自己的需要进行修改使用,学的东西死板。

这道题有个坑就是它没根没爹,所以就需要前向星建双向边,那么为了防止死循环就需要标记已经搜索过的边,双向边的话根就爱谁谁了,怎么着建出来的树都是合法的,注意到这些之后就正常的建树,查找LCA就行了(树上倍增),注意好细节就没问题,然后就到差分出场的时候了,怎么差分呢,算是利用树,一直向上传标记吧,那么就需要保证上传的时候只影响从该点到LCA路径上经过的点,把+1传过去,并且LCA只加一次,这么想的话只要在这两个点上+1,把+1标记上传,那路上经过的点就都会得到+1标记,并+1,这时候LCA加两次的问题就出现了,那一个-1就会抵消掉多加的一,这时候路径上的点就都搞定了,可是LCA上加一的点还会传给它的父亲,那同理给他的父亲也-1这件事情就完美解决了。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cmath>
 4 #include<cstring>
 5 #define maxn 300050
 6 using namespace std;
 7 struct shu{
 8     int zhong,qian;
 9 }qxx[2*maxn];
10 int n,js,m;
11 int a[maxn],head[maxn],tg[maxn],deep[maxn],pd[maxn];
12 int f[maxn][30];
13 void add(int u,int v)
14 {
15     qxx[++js].zhong=v;
16     qxx[js].qian=head[u];
17     head[u]=js;
18     return ;
19 }
20 void dfsf(int x)
21 {
22     pd[x]=1;
23     for(int i=head[x];i;i=qxx[i].qian)
24     {
25         int ls=qxx[i].zhong;
26         if(pd[ls]==0)
27         {
28             deep[ls]=deep[x]+1;
29             f[ls][0]=x;
30             for(int j=1;j<=m;++j)
31                 f[ls][j]=f[f[ls][j-1]][j-1];
32             dfsf(ls);
33         }
34     }
35     return ;
36 }
37 int lca(int x,int y)
38 {
39     if(deep[x]>deep[y])  swap(x,y);
40     for(int i=m;i>=0;--i)
41         if(deep[f[y][i]]>=deep[x])  y=f[y][i];
42     if(x==y)  return x;
43     for(int i=m;i>=0;--i)
44         if(f[x][i]!=f[y][i])
45         {
46             x=f[x][i];
47             y=f[y][i];
48         }
49     return f[x][0];
50 }
51 void dfs(int x)
52 {
53     pd[x]=1;
54     for(int i=head[x];i;i=qxx[i].qian)
55     {
56         int ls=qxx[i].zhong;
57         if(pd[ls]==0)
58         {
59             dfs(ls);
60             tg[x]+=tg[ls];
61         }
62     }
63     return ;
64 }
65 int main()
66 {
67     scanf("%d",&n);  m=(int)(log(n)/log(2))+2;
68     for(int i=1;i<=n;++i)  scanf("%d",&a[i]);
69     for(int i=1;i<n;++i)
70     {
71         int x,y;  scanf("%d%d",&x,&y);
72         add(x,y);  add(y,x);
73     }
74     deep[1]=1;  dfsf(1);
75     memset(pd,0,sizeof(pd));
76     for(int i=1;i<n;++i)
77     {
78         int ls=lca(a[i],a[i+1]);
79         tg[a[i]]+=1;  tg[a[i+1]]+=1;
80         tg[ls]-=1;  tg[f[ls][0]]-=1;
81     }
82     dfs(1);
83     tg[a[1]]++;
84     for(int i=1;i<=n;++i)  printf("%d\n",tg[i]-1);
85     return 0;
86 }

原文地址:https://www.cnblogs.com/hzjuruo/p/11296007.html

时间: 2024-10-09 00:08:14

[BAOJ3631]松鼠的新家的相关文章

BZOJ3631 松鼠的新家(树链剖分)

题目链接 松鼠的新家 差不多可以说是树链剖分的模板题了,直接维护即可. 1 #include <bits/stdc++.h> 2 3 using namespace std; 4 5 #define REP(i,n) for(int i(0); i < (n); ++i) 6 #define rep(i,a,b) for(int i(a); i <= (b); ++i) 7 #define dec(i,a,b) for(int i(a); i >= (b); --i) 8 #

[填坑]树上差分 例题:[JLOI2014]松鼠的新家(LCA)

今天算是把LCA这个坑填上了一点点,又复习(其实是预习)了一下树上差分.其实普通的差分我还是会的,树上的嘛,也是懂原理的就是没怎么打过. 我们先来把树上差分能做到的看一下: 1.找所有路径公共覆盖的边 例题:[NOIP2015]运输计划 (然而我还没过就先不讲了) 反正就是中间有一步要求一条边被所有计划公共覆盖. 那么怎么求它呢?暴力(滚粗).我们有一个非常好的方法就是树上差分(记录tmp为差分数组) 询问操作为从叶子节点的权值向上累加到root 在一条路径u→ v,如果tmp[u]++,那么我

【BZOJ 3631】 [JLOI2014]松鼠的新家

3631: [JLOI2014]松鼠的新家 Time Limit: 10 Sec Memory Limit: 128 MB Submit: 681 Solved: 329 [Submit][Status][Discuss] Description 松鼠的新家是一棵树,前几天刚刚装修了新家,新家有n个房间,并且有n-1根树枝连接,每个房间都可以相互到达,且俩个房间之间的路线都是唯一的.天哪,他居然真的住在"树"上.松鼠想邀请小熊维尼前来参观,并且还指定一份参观指南,他希望维尼能够按照他的

P3258 松鼠的新家

松鼠的新家 洛谷链接 尽管标签是省选/NOI-,但提交的通过率已经高到三分之一了. 但它仍旧是一个省选/NOI-的题. 大致题意就是按输入的顺序走一棵树,看每个节点经过多少次.问题就相当于把一条链上的所有节点权值都加一,最后统计每个点的权值. 但这样的时间复杂度比较高,所以我们可以把这条链的头节点和尾节点的权值都加一,然后把它们的LCA减一,它们LCA的父亲节点减一.之后每次通记每个节点都统计该节点及其子树的权值和,就是这个节点被经过的次数. 而题目要求在最后的终点不需要计算,所以我们可以把"这

BZOJ 3631: [JLOI2014]松鼠的新家( 树链剖分 )

裸树链剖分... ------------------------------------------------------------------- #include<bits/stdc++.h> using namespace std; const int maxn = 300009; struct edge { int to; edge* next; } E[maxn << 1], *pit = E, *head[maxn]; inline void add(int u,

P3258 [JLOI2014]松鼠的新家

P3258 [JLOI2014]松鼠的新家倍增lca+树上差分,从叶子节点向根节点求前缀和,dfs求子树和即可,最后,把每次的起点和终点都. 1 #include<iostream> 2 #include<cstdio> 3 #include<queue> 4 #include<algorithm> 5 #include<cmath> 6 #include<ctime> 7 #include<set> 8 #include

【洛谷】【lca+树上差分】P3258 [JLOI2014]松鼠的新家

[题目描述:] 松鼠的新家是一棵树,前几天刚刚装修了新家,新家有n(2 ≤ n ≤ 300000)个房间,并且有n-1根树枝连接,每个房间都可以相互到达,且俩个房间之间的路线都是唯一的.天哪,他居然真的住在”树“上. 松鼠想邀请小熊维尼前来参观,并且还指定一份参观指南,他希望维尼能够按照他的指南顺序,先去a1,再去a2,......,最后到an,去参观新家.可是这样会导致维尼重复走很多房间,懒惰的维尼不停地推辞.可是松鼠告诉他,每走到一个房间,他就可以从房间拿一块糖果吃. 维尼是个馋家伙,立马就

[Luogu] P3258 [JLOI2014]松鼠的新家

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

【bzoj3631】【JLOI2014】松鼠的新家

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