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

题目描述

松鼠的新家是一棵树,前几天刚刚装修了新家,新家有n个房间,并且有n-1根树枝连接,每个房间都可以相互到达,且俩个房间之间的路线都是唯一的。天哪,他居然真的住在”树“上。

松鼠想邀请小熊维尼前来参观,并且还指定一份参观指南,他希望维尼能够按照他的指南顺序,先去a1,再去a2,......,最后到an,去参观新家。可是这样会导致维尼重复走很多房间,懒惰的维尼不停地推辞。可是松鼠告诉他,每走到一个房间,他就可以从房间拿一块糖果吃。

维尼是个馋家伙,立马就答应了。现在松鼠希望知道为了保证维尼有糖果吃,他需要在每一个房间各放至少多少个糖果。

因为松鼠参观指南上的最后一个房间an是餐厅,餐厅里他准备了丰盛的大餐,所以当维尼在参观的最后到达餐厅时就不需要再拿糖果吃了。

题目分析

树剖,树状数组维护

相邻两个计划点之间的点权++,第2~n个计划点点权-1。

Code

#include<iostream>
#include<cstdio>
using namespace std;

const int MAXN = 300000 + 5;

struct Edge {
    int nxt;
    int to;
} l[MAXN<<1];

int n,root;
int head[MAXN],cnt;
int deep[MAXN],fa[MAXN],siz[MAXN],heavy[MAXN];
int id[MAXN],tot;
int a[MAXN],c[MAXN],top[MAXN];

inline void add(int x,int y) {
    cnt++;
    l[cnt].nxt = head[x];
    l[cnt].to = y;
    head[x] = cnt;
    return;
}

void dfs1(int x,int from) {
    deep[x] = deep[from] + 1;
    fa[x] = from;
    int tmp = 0;
    siz[x] = 1;
    for(int i = head[x];i;i = l[i].nxt) {
        if(l[i].to == from) continue;
        dfs1(l[i].to,x);
        siz[x] += siz[l[i].to];
        if(siz[l[i].to] > tmp) {
            tmp = siz[l[i].to];
            heavy[x] = l[i].to;
        }
    }
    return;
}

void dfs2(int x,int tp,int from) {
    id[x] = ++tot;
    top[x] = tp;
    if(!heavy[x]) return;
    dfs2(heavy[x],tp,x);
    for(int i = head[x];i;i = l[i].nxt) {
        if(l[i].to == from || l[i].to == heavy[x]) continue;
        dfs2(l[i].to,l[i].to,x);
    }
    return;
}

inline int lowbit(int x) {
    return x & (-x);
}

inline void modify(int x,int y,int v) {
    for(int i = x;i <= n;i += lowbit(i)) c[i]+=v;
    for(int i = y+1;i <= n;i += lowbit(i)) c[i]-=v;
    return;
}

inline int query(int x) {
    int res = 0;
    for(int i = x;i;i -= lowbit(i)) res += c[i];
    return res;
}

inline void wayadd(int x,int y,int v) {
    while(top[x] != top[y]) {
        if(deep[top[x]] < deep[top[y]]) swap(x,y);
        modify(id[top[x]],id[x],v);
        x = fa[top[x]];
    }
    if(deep[x] > deep[y]) swap(x,y);
    modify(id[x],id[y],v);
    return;
}

int main() {
    scanf("%d",&n);
    for(int i = 1;i <= n;i++) {
        scanf("%d",&a[i]);
    }
    root = a[1];
    int x,y;
    for(int i = 1;i < n;i++) {
        scanf("%d%d",&x,&y);
        add(x,y);add(y,x);
    }
    dfs1(root,0);
    dfs2(root,root,0);
    for(int i = 1;i < n;i++) {
        wayadd(a[i],a[i+1],1);
        wayadd(a[i+1],a[i+1],-1);
    }
    for(int i = 1;i <= n;i++) {
        printf("%d\n",query(id[i]));
    }
    return 0;
}

原文地址:https://www.cnblogs.com/floatiy/p/9750124.html

时间: 2024-08-29 13:19:23

[Luogu] 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

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

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

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

Luogu P3258 题意就是对于一棵树,要求按照给出的顺序对每一个节点进行访问,记录每一个节点被经过的次数:特别地,我们认为只有从一个节点往外走才能被认为是经过一次.(最后一句话非常重要,仔细理解题意) 前置知识:树链剖分,差分. 最开始看到这道题我是打算使用树链剖分+线段树来做的. 但是我发现这个答案只需要每一个房间的糖果数--也就是说只需要区间修改+单点查询. 如果使用线段树的话,可能造成大量的空间浪费,而且常数也不小. 所以,我选择了使用树链剖分+差分进行统计. 由于差分写得比较少,本

题解 P3258 【[JLOI2014]松鼠的新家】

这道题可以说是树剖模板题...然而我一直WA10找不出错误...后来才发现是手抖打少了一个两个字符... 其实题目说的就是给你一颗树和一个遍历顺序,然后按照遍历顺序更新路径的值,最后查询所有节点的值. 其实这种题用树上差分会更理想,但是为了练树剖也就写了树剖. AC代码如下: 1097ms 56696kb #include<bits/stdc++.h> using namespace std; namespace StandardIO { template<typename T>i

BZOJ3631: [JLOI2014]松鼠的新家

传送门 树上的差分优化,很简单的一道题,应该属于NOIP2015TGD2T3的子问题. //BZOJ 3631 //by Cydiater //2016.10.25 #include <iostream> #include <cstdlib> #include <cstdio> #include <cmath> #include <ctime> #include <cstring> #include <string> #i