[SCOI2015]情报传递[树剖+主席树]

[SCOI2015]情报传递

题意大概就是 使得在 \(i\) 时刻加入一个情报员帮您传情报 然后询问 \(x,y,c\) 指 \(x\)到\(y\)多少个人有风险…(大于c)的都有风险…每天风险值+1

看起来…不太可做…

每次要整棵树+1复杂度也需要\(log^2\)的树套树吧

但是显然不用啊 查询的时候减掉就可以了…

所以直接树剖上面无脑主席树就可以了啊…

#include <bits/stdc++.h>
// #define int long long
#define rep(a , b , c) for(int a = b ; a <= c ; ++ a)
#define Rep(a , b , c) for(int a = b ; a >= c ; -- a)
#define go(u) for(int i = G.head[u] , v = G.to[i] , w = G.dis[i] ; i ; v = G.to[i = G.nxt[i]] , w = G.dis[i])

using namespace std ;
using ll = long long ;
using pii = pair < int , int > ;
using vi = vector < int > ;

int read() {
  int x = 0 ; bool f = 1 ; char c = getchar() ;
  while(c < 48 || c > 57) { if(c == '-') f = 0 ; c = getchar() ; }
  while(c > 47 && c < 58) { x = (x << 1) + (x << 3) + (c & 15) ; c = getchar() ; }
  return f ? x : -x ;
}

template <class T> void print(T x , char c = '\n') {
  static char st[100] ; int stp = 0 ;
  if(! x) { putchar('0') ; }
  if(x < 0) { x = -x ; putchar('-') ; }
  while(x) { st[++ stp] = x % 10 ^ 48 ; x /= 10 ; }
  while(stp) { putchar(st[stp --]) ; } putchar(c) ;
}

template <class T> void cmax(T & x , T y) { x < y ? x = y : 0 ; }
template <class T> void cmin(T & x , T y) { x > y ? x = y : 0 ; }

const int _N = 1e6 + 10 ;
struct Group {
  int head[_N] , nxt[_N << 1] , to[_N] , dis[_N] , cnt = 1 ;
  Group () { memset(head , 0 , sizeof(head)) ; }
  void add(int u , int v , int w = 1) { nxt[++ cnt] = head[u] ; to[cnt] = v ; dis[cnt] = w ; head[u] = cnt ; }
} G;

const int N = 2e5 + 10  ;
typedef int arr[N] ;
int n , q ;
arr X , Y , c , rt , sz , fa , son , d , val ;
int root = 0 ;
void dfs(int u) {
    sz[u] = 1 ; go(u) {
        d[v] = d[u] + 1 ;
        dfs(v) ; sz[u] += sz[v] ;
        if(sz[v] > sz[son[u]]) son[u] = v ;
    }
}
int idx = 0 ;
arr top , id ;
void dfs(int u , int t){
    top[u] = t ; id[u] = ++ idx ;
    if(son[u]) dfs(son[u] , t) ;
    go(u) if(v ^ son[u]) dfs(v , v) ;
}
int cnt = 0 ;
int ls[N << 5] , rs[N << 5] , sum[N << 5] ;
void upd(int pre , int & p , int l , int r , int pos) {
    ls[p = ++ cnt] = ls[pre] ;
    rs[p] = rs[pre] ;
    sum[p] = sum[pre] + 1 ;
    if(l == r) return ;
    int mid = l + r >> 1 ;
    pos <= mid ? upd(ls[pre] , ls[p] , l , mid , pos) : upd(rs[pre] , rs[p] , mid + 1 , r , pos) ;
}
int query(int L , int R , int l , int r , int x) {
    if(l == r) return sum[R] - sum[L] ;
    int mid = l + r >> 1 ;
    if(x <= mid) return query(ls[L] , ls[R] , l , mid , x) ;
    return sum[ls[R]] - sum[ls[L]] + query(rs[L] , rs[R] , mid + 1 , r , x) ;
}
void build(int u) {
    upd(rt[fa[u]] , rt[u] , 1 , q , val[u]) ; go(u) build(v) ;
}
int Lca(int x , int y) {
    while(top[x] != top[y]) {
        if(d[top[x]] < d[top[y]]) swap(x , y) ;
        x = fa[top[x]] ;
    }
    return d[x] < d[y] ? x : y ;
}
signed main() {
    n = read() ;
    rep(i , 1 , n) { fa[i] = read() ; if(! fa[i]) root = i ; else G.add(fa[i] , i) ; }
    q = read() ;
    rep(i , 1 , n) val[i] = q ;
    rep(i , 1 , q) {
        int op = read() ;
        if(op == 1) X[i] = read() , Y[i] = read() , c[i] = read() ;
        else val[read()] = i ;
    }
    dfs(root) ; dfs(root , root) ; build(root) ;
    rep(i , 1 , q) {
        if(! X[i]) continue ;
        int lca = Lca(X[i] , Y[i]) ;
        print(d[X[i]] + d[Y[i]] - (d[lca] << 1) + 1 , ' ') ;
        if(i - c[i] - 1 <= 0) { print(0) ; continue ; }
        print(query(rt[lca] , rt[X[i]] , 1 , q , i - c[i] - 1) + query(rt[lca] , rt[Y[i]] , 1 , q , i - c[i] - 1) + (val[lca] <= i - c[i] - 1)) ;
    }
    return 0 ;
}

原文地址:https://www.cnblogs.com/Isaunoya/p/12019645.html

时间: 2024-11-07 03:43:26

[SCOI2015]情报传递[树剖+主席树]的相关文章

归并树 划分树 可持久化线段树(主席树) 入门题 hdu 2665

如果题目给出1e5的数据范围,,以前只会用n*log(n)的方法去想 今天学了一下两三种n*n*log(n)的数据结构 他们就是大名鼎鼎的 归并树 划分树 主席树,,,, 首先来说两个问题,,区间第k大 ,,,, 这个问题的通用算法是 划分树,, 说白一点就是把快速排序的中间结果存起来, 举个栗子 原数列 4 1 8 2 6 9 5 3 7 sorted 1 2 3 4 5 6 7 8 9 ........................... qs[0] 4 1 8 2 6 9 5 3 7 q

[可持久化线段树(主席树)]

主席树 抛出问题 如题,给定N个整数构成的序列,将对于指定的闭区间查询其区间内的第K小值. 输入输出格式 输入格式: 第一行包含两个正整数N.M,分别表示序列的长度和查询的个数. 第二行包含N个整数,表示这个序列各项的数字. 接下来M行每行包含三个整数l, r, kl,r,k , 表示查询区间[l, r][l,r]内的第k小值. 输出格式: 输出包含k行,每行1个整数,依次表示每一次查询的结果 解决问题 主席树(可持久化线段树)法 于是针对这个问题,新的数据结构诞生了,也就是主席树. 主席树本名

可持续化线段树(主席树)

什么是主席树 可持久化数据结构(Persistent data structure)就是利用函数式编程的思想使其支持询问历史版本.同时充分利用它们之间的共同数据来减少时间和空间消耗. 因此可持久化线段树也叫函数式线段树又叫主席树. 可持久化数据结构 在算法执行的过程中,会发现在更新一个动态集合时,需要维护其过去的版本.这样的集合称为是可持久的. 实现持久集合的一种方法时每当该集合被修改时,就将其整个的复制下来,但是这种方法会降低执行速度并占用过多的空间. 考虑一个持久集合S. 如图所示,对集合的

神奇的树(主席树思想的应用)

主席树这个概念应该不陌生吧!恩?不会, 戳这里. 主席树(函数式线段树)用的是函数思想,一个节点开数组用来保存自己的左右节点,这样节省许多不必要的空间,还可以保存许多历史状态.而这里我们用的是主席树的函数思想来实现. 上题:http://acm.hdu.edu.cn/showproblem.php?pid=5444 题目大意: 给你一个序列,第一个数为二叉树根节点,之后每个数往上加节点,且保证左节点小于根节点,且保证右节点大于根节点.且每个节点最多有2个子节点.然后再查询位置,每往左找输出一个E

BZOJ4034 树上操作(树剖 线段树大模板)

BZOJ4034 long long 是大坑点 貌似long long 跟int 乘起来会搞事情?... A了这题线段树和树剖的基础OK 嘛 重点过掉的还是线段树区间更新的lazy tag吧 #include<cstdio> #include<cstring> #define N 100001 using namespace std; struct ed{ int nxt,to; }e[N*2]; int ne=0,head[N]; long long int w0[N]; str

poj3728The merchant树剖+线段树

如果直接在一条直线上,那么就建线段树 考虑每一个区间维护最小值和最大值和答案,就符合了合并的条件,一个log轻松做 那么在树上只要套一个树剖就搞定了,多一个log也不是问题 注意考虑在树上的话每一条链都有可能是正着被用和反着被用,所以存两个答案 所以维护信息只需要一个merge和一个reverse 代码如下: 1 #include <cstdio> 2 #include <iostream> 3 #define mid (l+r>>1) 4 using namespac

POJ3417Network(LCA+树上查分||树剖+线段树)

Yixght is a manager of the company called SzqNetwork(SN). Now she's very worried because she has just received a bad news which denotes that DxtNetwork(DN), the SN's business rival, intents to attack the network of SN. More unfortunately, the origina

洛谷 [P3834] 可持久化线段树(主席树)

主席树可以存储线段树的历史状态,空间消耗很大,一般开45n即可 #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <cstdlib> #include <queue> #define lson l, mid #define rson mid+1, r #define ll long long using name

静态可持久化线段树(主席树)

题目背景 这是个非常经典的主席树入门题——静态区间第K小 数据已经过加强,请使用主席树.同时请注意常数优化 题目描述 如题,给定N个正整数构成的序列,将对于指定的闭区间查询其区间内的第K小值. 输入输出格式 输入格式: 第一行包含两个正整数N.M,分别表示序列的长度和查询的个数. 第二行包含N个正整数,表示这个序列各项的数字. 接下来M行每行包含三个整数 l, r, kl,r,k , 表示查询区间 [l, r][l,r] 内的第k小值. 输出格式: 输出包含k行,每行1个正整数,依次表示每一次查