【JLOI2014】松鼠的新家(树上差分)

题目链接:https://loj.ac/problem/2236

做法

其实没有什么好说的,这个题就属于那种看起来没什么细节,实际也没有什么细节的题,但是却把我埋了,关于这道题的细节请读者自悟

然后就是树上差分,适用于多次操作一次查询的题

把路径$(u, v)$的边权加$val$的操作:

  • $diff[u] += val$, $diff[v] += val$
  • $diff[lca(u, v)] -= val$, $diff[fa[lca(u, v)]] -= val$

#include <iostream>
#include <cstdio>
#define Re register
using namespace std;
inline int read() {
    int x = 0;
    char ch = getchar();
    while (!isdigit(ch)) ch = getchar();
    while (isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar();
    return x;
}
const int maxN = 300005;
int N, diff[maxN], ord[maxN], num[maxN];
struct Edge {
    int nxt, to;
} e[maxN << 1];
int cnte = 1, head[maxN];
inline void add_Edge(int i, int j) { e[++cnte].nxt = head[i], e[cnte].to = j, head[i] = cnte; }
int dep[maxN], anc[maxN][21];
void dfslca(int u, int fa) {
    dep[u] = dep[fa] + 1;
    anc[u][0] = fa;
    for (int i = head[u]; i; i = e[i].nxt) {
        if (e[i].to == fa)
            continue;
        dfslca(e[i].to, u);
    }
}
inline int LCA(int x, int y) {
    if (dep[x] < dep[y])
        x ^= y ^= x ^= y;
    for (int i = 18; i >= 0; --i)
        if (dep[anc[x][i]] >= dep[y])
            x = anc[x][i];
    if (x == y)
        return x;
    for (int i = 18; i >= 0; --i)
        if (anc[x][i] != anc[y][i])
            x = anc[x][i], y = anc[y][i];
    return anc[x][0];
}
void dfs(int u, int fa) {
    for (Re int v, i = head[u]; i; i = e[i].nxt) {
        if ((v = e[i].to) == fa)
            continue;
        dfs(v, u);
        diff[u] += diff[v];
    }
}
int main() {
    N = read();
    for (Re int i = 1; i <= N; ++i) ord[i] = read(), num[ord[i]] = i;
    for (Re int i = 1, u, v; i < N; ++i) {
        u = read(), v = read();
        add_Edge(u, v), add_Edge(v, u);
    }
    dfslca(1, 0);
    for (int i = 1; i <= 18; ++i)
        for (int u = 1; u <= N; ++u) anc[u][i] = anc[anc[u][i - 1]][i - 1];
    for (Re int i = 1; i < N; ++i) {
        Re int x = ord[i], y = ord[i + 1];
        Re int lca = LCA(x, y);
        diff[anc[lca][0]] -= 1, diff[lca] -= 1;
        diff[x] += 1, diff[y] += 1;
    }
    dfs(1, 0);
    for (int i = 1; i <= N; ++i) printf("%d\n", (num[i] == 1) ? diff[i] : (diff[i] - 1));
    return 0;
}

原文地址:https://www.cnblogs.com/blog-fgy/p/12363101.html

时间: 2024-11-08 13:18:40

【JLOI2014】松鼠的新家(树上差分)的相关文章

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

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

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

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

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

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

【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,

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

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

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

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

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]松鼠的新家

题解:树上差分就可以了 论看懂题意的重要性 最后-1的时候处理错了,WA了几发 #include<iostream> #include<cstdio> #include<cstring> using namespace std; const int maxn=600009; const int Inf=1000000000; int n,m; int a[maxn]; int cntedge; int head[maxn]; int to[maxn<<1],