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 <queue>
using namespace std;
typedef long long ll;
#define enter putchar('\n')
#define space putchar(' ')
template <class T>
void read(T &x){
    char c;
    bool op = 0;
    while(c = getchar(), c > '9' || c < '0')
    if(c == '-') op = 1;
    x = c - '0';
    while(c = getchar(), c >= '0' && c <= '9')
    x = x * 10 + c - '0';
    if(op) x = -x;
}
template <class T>
void write(T x){
    if(x < 0) putchar('-'), x = -x;
    if(x >= 10) write(x / 10);
    putchar('0' + x % 10);
}

const int N = 300005;
int n, a[N], ans[N], dep[N], stk[N], top, pos[N], seq[2 * N][20], tot, lg[2 * N], fa[N];
int ecnt, adj[N], nxt[2 * N], go[2 * N];

void add(int u, int v){
    go[++ecnt] = v;
    nxt[ecnt] = adj[u];
    adj[u] = ecnt;
}
void dfs(int u){
    seq[++tot][0] = u, pos[u] = tot, stk[++top] = u;
    for(int e = adj[u], v; e; e = nxt[e])
    if(v = go[e], v != fa[u]){
        fa[v] = u, dep[v] = dep[u] + 1;
        dfs(v);
        seq[++tot][0] = u;
    }
}
int Min(int u, int v){
    return dep[u] < dep[v] ? u : v;
}
void init(){
    for(int i = 1, j = 0; i <= tot; i++)
    lg[i] = i == (1 << (j + 1)) ? ++j : j;
    for(int j = 1; (1 << j) <= tot; j++)
    for(int i = 1; i + (1 << j) - 1 <= tot; i++)
        seq[i][j] = Min(seq[i][j - 1], seq[i + (1 << (j - 1))][j - 1]);
}
int lca(int u, int v){
    int l = pos[u], r = pos[v];
    if(l > r) swap(l, r);
    int j = lg[r - l + 1];
    return Min(seq[l][j], seq[r - (1 << j) + 1][j]);
}
void path_modify(int u, int v, int x){
    int anc = lca(u, v);
    ans[u] += x, ans[v] += x, ans[anc] -= x, ans[fa[anc]] -= x;
}

int main(){

    read(n);
    for(int i = 1; i <= n; i++) read(a[i]);
    for(int i = 1, u, v; i < n; i++)
    read(u), read(v), add(u, v), add(v, u);
    dfs(1);
    init();
    for(int i = 1; i < n; i++)
    path_modify(a[i], a[i + 1], 1), path_modify(a[i + 1], a[i + 1], -1);
    for(int i = top; i; i--)
    ans[fa[stk[i]]] += ans[stk[i]];
    for(int i = 1; i <= n; i++)
    write(ans[i]), enter;

    return 0;
}

原文地址:https://www.cnblogs.com/RabbitHu/p/BZOJ3631.html

时间: 2024-11-05 18:54:57

BZOJ 3631 [JLOI2014]松鼠的新家 | 树上差分的相关文章

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,

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

BZOJ 3631 JLOI2014 松鼠的新家 树链剖分/LCA

题目大意:给定一棵无根树和一个序列,在这个序列上依次遍历,求每个点的访问次数(最后一个点的访问次数要-1) 树链剖分的裸题--考场上我还是一个弱渣,啥也不会,暴力得了50分,剩下两道题爆零了...而且30W深搜爆栈,人生第一次手写了系统栈.. 回来因为这题的原因去学了树链剖分 结果没学明白 每条重链单独开了一棵线段树 常数大的要死 高一时写的代码...还是别看了,拿去对拍可以,阅读性欠佳 #include<stdio.h> #include<stdlib.h> #include&l

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

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

[填坑]树上差分 例题:[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

[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,去参观新家.可是这样会导致维尼重复走很多房间,懒惰的维尼不停地推辞.可是松鼠告诉他,每走到一个房间,他就可以从房间拿一块糖果吃. 维尼是个馋家伙,立马就答应了.现在松鼠希望知道为了保证维尼有