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

【题目描述:】

松鼠的新家是一棵树,前几天刚刚装修了新家,新家有n(2 ≤ n ≤ 300000)个房间,并且有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

输入输出样例

【算法分析:】

根据小熊维尼走的路径记录每个点走过的次数,就是需要放的糖果数目

从点i走到点j, 可以看做:从i走到lca(i, j)再走到j是最短的路径。

而如何把i -> lca, j -> lca的点全部自增1呢?

可以用树上差分来维护,设定一个一维数组cha

对于从i到j的一条路径,

cha[i]++; cha[j]++;
cha[lca(i, j)]--;
cha[f[lca(i, j)][0]]--;//点i, j最近公共祖先的父节点

然后从根节点开始dfs这棵树,对cha数组做一遍前缀和

最后对所有既是起点也是重点的点的经过次数-1.

【代码:】

 1 //P3258 [JLOI2014]松鼠的新家
 2 #include<iostream>
 3 #include<cstdio>
 4 #include<cstring>
 5 using namespace std;
 6
 7 const int MAXN = 300000 + 1;
 8 const int K = 29 + 1;
 9
10 int n, a[MAXN], ans[MAXN];
11 int cha[MAXN];
12 int deep[MAXN], f[MAXN][K];
13 int edge_num, head[MAXN];
14 struct Edge {
15     int to, next;
16 }h[MAXN << 1];
17
18 inline int read() {
19     int x = 0, f = 1; char ch = getchar();
20     while(ch < ‘0‘ || ch > ‘9‘) {
21         if(ch == ‘-‘) f = -1;
22         ch = getchar();
23     }
24     while(ch >= ‘0‘ && ch <= ‘9‘)
25         x = (x << 3) + (x << 1) + ch - 48, ch = getchar();
26     return x * f;
27 }
28
29 inline void Add(int from, int to) {
30     h[++edge_num].to = to;
31     h[edge_num].next = head[from];
32     head[from] = edge_num;
33 }
34
35 void build(int u) {
36     for(int i = head[u]; i != -1; i = h[i].next) {
37         if(!deep[h[i].to]) {
38             deep[h[i].to] = deep[u] + 1;
39             f[h[i].to][0] = u;
40             build(h[i].to);
41         }
42     }
43 }
44
45 inline void fill() {
46     for(int j = 1; j < K; ++j)
47     for(int i = 1; i <= n; ++i)
48         f[i][j] = f[f[i][j - 1]][j - 1];
49 }
50
51 int lca(int a, int b) {
52     if(deep[a] > deep[b])
53         swap(a, b);
54     for(int i = K - 1; i >= 0; --i)
55         if(deep[f[b][i]] >= deep[a]) b = f[b][i];
56     if(a == b) return a;
57     for(int i = K - 1; i >= 0; --i) {
58         if(f[b][i] != f[a][i]) {
59             a = f[a][i], b = f[b][i];
60         }
61     }
62     return f[b][0];
63 }
64
65 void dfs(int u) {
66     for(int i = head[u]; i != -1; i = h[i].next) {
67         if(h[i].to == f[u][0]) continue;
68         dfs(h[i].to);
69         cha[u] += cha[h[i].to];
70     }
71 }
72
73 int main() {
74     memset(head, -1, sizeof(head));
75     n = read();
76     for(int i = 1; i <= n; ++i) a[i] = read();
77     for(int i = 1; i < n; ++i) {
78         int x = read(), y = read();
79         Add(x, y);
80         Add(y, x);
81     }
82     deep[a[n]] = 1;
83     build(a[n]);
84     fill();
85     for(int i = 2; i <= n; ++i) {
86         int com = lca(a[i], a[i - 1]);
87         ++cha[a[i]], ++cha[a[i - 1]];
88         --cha[com], --cha[f[com][0]];
89     }
90     dfs(a[n]);
91     for(int i = 2; i <= n; ++i)
92         --cha[a[i]];
93     for(int i = 1; i <= n; ++i)
94         printf("%d\n", cha[i]);
95 }

原文地址:https://www.cnblogs.com/devilk-sjj/p/9050195.html

时间: 2024-11-05 21:58:35

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

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

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

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

[JLOI2014]松鼠的新家 (树剖)

题目 P3258 [JLOI2014]松鼠的新家 解析 非常裸的一道树剖题 链上修改+单点查询的板子 记录一下所经过的点\(now[i]\),每次更新\(now[i-1]到now[i]\) 我们链上更新时上一次到的终点,是这一次一次更新的起点,又因为在\(a_n\)处可以不放糖,所以我们每次链上更新完成后,在这条链的终点位置处糖数\(-1\). 然后套板子直接做 代码 #include <bits/stdc++.h> using namespace std; const int N = 2e6

[填坑]树上差分 例题:[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根树枝连接,每个房间都可以相互到达,且俩个房间之间的路线都是唯一的.天哪,他居然真的住在"树"上.松鼠想邀请小熊维尼前来参观,并且还指定一份参观指南,他希望维尼能够按照他的

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,

BZOJ3631: [JLOI2014]松鼠的新家 树链剖分/lca/树上查分

求n次lca后树上差分. 每次移动时在起始点和终点各打一个start标记,在LCA和LCA的父节点处各上打一个end标记,然后深搜,start标记一直上传,遇到end标记就停止,最后再处理一下就行 % PoPoQQQ大爷 #include<bits/stdc++.h> #define rep(i,l,r) for(int i=l;i<=r;i++) #define N 310000 using namespace std; int head[N],dep[N],sz[N],son[N],

BZOJ 3631 [JLOI2014]松鼠的新家 | 树上差分

链接 BZOJ 3631 题解 看起来是树剖?实际上树上差分就可以解决-- 当要给一条路径(u, v) +1的时候,给d[u] += 1, d[v] += 1, d[lca(u, v)] -= 1, d[fa[lca(u, v)]] -= 1. 注意这道题中路径的终点是不 +1的. #include <cstdio> #include <cmath> #include <cstring> #include <algorithm> #include <q

【洛谷P3258】松鼠的新家

很好的一道题 LCA+树上前缀和 sum数组是前缀和数组, 分类讨论一下, 1.访问到一个点p1,若下一个点p2需要往儿子下面找的话,那么lca就是这个点p1,则sum[p1]--; sum[p2]++; 2.访问到一个点p1,若下一个点p2需要往父亲上找的话,那么lca就是p2,则sum[f[p2][0]]--; sum[f[p1][0]]++ 3.访问到一个点p1,若下一个点p2需要跨过p1,p2的lca的话,则sum[lca]--; sum[f[lca][0]]--; sum[p1]++;