【bzoj4448】[Scoi2015]情报传递 主席树

题目描述

奈特公司是一个巨大的情报公司,它有着庞大的情报网络。情报网络中共有n名情报员。每名情报员口J-能有若T名(可能没有)下线,除1名大头日外其余n-1名情报员有且仅有1名上线。奈特公司纪律森严,每名情报员只能与自己的上、下线联系,同时,情报网络中仟意两名情报员一定能够通过情报网络传递情报。奈特公司每天会派发以下两种任务中的一个任务:
1.搜集情报:指派T号情报员搜集情报
2.传递情报:将一条情报从X号情报员传递给Y号情报员
情报员最初处于潜伏阶段,他们是相对安全的,我们认为此时所有情报员的危险值为0;-旦某个情报员开始搜集情报,他的危险值就会持续增加,每天增加1点危险值(开始搜集情报的当天危险值仍为0,第2天危险值为1,第3天危险值为2,以此类推)。传递情报并不会使情报员的危险值增加。
为了保证传递情报的过程相对安全,每条情报都有一个风险控制值C。余特公司认为,参与传递这条情报的所有情报员中,危险值大于C的情报员将对该条情报构成威胁。现在,奈特公司希望知道,对于每个传递情报任务,参与传递的情报员有多少个,其中对该条情报构成威胁的情报员有多少个。

输入

第1行包含1个正整数n,表示情报员个数。
笫2行包含n个非负整数,其中第i个整数Pi表示i号情报员上线的编号。特别地,若Pi=0,表示i号情报员是大头目。
第3行包含1个正整数q,表示奈特公司将派发q个任务(每天一个)。
随后q行,依次描述q个任务。
每行首先有1个正整数k。若k=1,表示任务是传递情报,随后有3个正整数Xi、Yi、Ci,依次表示传递情报的起点、终点和风险控制值;若k=2,表示任务是搜集情报,随后有1个正整数Ti,示搜集情报的情报员编号。

输出

对于每个传递情报任务输出一行,应包含两个整数,分别是参与传递情报的情报员个数和对该条情报构成威胁的情报员个数。
输出的行数应等于传递情报任务的个数,每行仅包含两个整数,用一个空格隔开。输出不应包含多余的空行和空格。

样例输入

7
0 1 1 2 2 3 3
6
1 4 7 0
2 1
2 4
2 7
1 4 7 1
1 4 7 3

样例输出

5 0
5 2
5 1


题目大意

给出一棵树,有两种操作:1.标记一个点;2.询问两点间路径长度及路径上上有多少个点在 当前时间减去ci 之前被标记过

题解

主席树

看懂了题就好做多了。

考虑到修改操作比较麻烦,由于c>0,表明后面对前面没有影响,可以调换顺序。

我们可以离线处理,先将所有点标记上(即赋权值为标记时间),然后离线查找即可。

之后就是套路,点x在fa[x]之上建立主席树,查询x、y时相当于1->x + 1->y - 1->lca(x,y) - 1->fa[lca(x,y)]。

注意一下 当前时间-ci<=0 时的特判

#include <cstdio>
#include <algorithm>
#define N 200010
using namespace std;
int m , head[N] , to[N] , next[N] , cnt , fa[N][20] , deep[N] , log[N] , opt[N] , x[N] , y[N] , c[N] , p[N];
int root[N] , ls[N * 20] , rs[N * 20] , si[N * 20] , tot;
void add(int x , int y)
{
    to[++cnt] = y , next[cnt] = head[x] , head[x] = cnt;
}
void ins(int p , int l , int r , int x , int &y)
{
    y = ++tot , si[y] = si[x] + 1;
    if(l == r) return;
    int mid = (l + r) >> 1;
    if(p <= mid) rs[y] = rs[x] , ins(p , l , mid , ls[x] , ls[y]);
    else ls[y] = ls[x] , ins(p , mid + 1 , r , rs[x] , rs[y]);
}
void dfs(int x)
{
    int i;
    if(p[x]) ins(p[x] , 1 , m , root[fa[x][0]] , root[x]);
    else root[x] = root[fa[x][0]];
    for(i = 1 ; i <= log[deep[x]] ; i ++ ) fa[x][i] = fa[fa[x][i - 1]][i - 1];
    for(i = head[x] ; i ; i = next[i]) deep[to[i]] = deep[x] + 1 , dfs(to[i]);
}
int getlca(int x , int y)
{
    int i;
    if(deep[x] < deep[y]) swap(x , y);
    for(i = log[deep[x] - deep[y]] ; ~i ; i -- )
        if(deep[x] - (1 << i) >= deep[y])
            x = fa[x][i];
    for(i = log[deep[x]] ; ~i ; i -- )
        if(deep[x] >= (1 << i) && fa[x][i] != fa[y][i])
            x = fa[x][i] , y = fa[y][i];
    return x == y ? x : fa[x][0];
}
int query(int p , int l , int r , int a , int b , int c , int d)
{
    if(l == r) return si[a] + si[b] - si[c] - si[d];
    int mid = (l + r) >> 1;
    if(p <= mid) return query(p , l , mid , ls[a] , ls[b] , ls[c] , ls[d]);
    else return query(p , mid + 1 , r , rs[a] , rs[b] , rs[c] , rs[d]) + si[ls[a]] + si[ls[b]] - si[ls[c]] - si[ls[d]];
}
int main()
{
    int n , i , f;
    scanf("%d%*d" , &n);
    for(i = 2 ; i <= n ; i ++ ) scanf("%d" , &fa[i][0]) , add(fa[i][0] , i) , log[i] = log[i >> 1] + 1;
    scanf("%d" , &m);
    for(i = 1 ; i <= m ; i ++ )
    {
        scanf("%d%d" , &opt[i] , &x[i]);
        if(opt[i] == 1) scanf("%d%d" , &y[i] , &c[i]);
        else if(!p[x[i]]) p[x[i]] = i;
    }
    dfs(1);
    for(i = 1 ; i <= m ; i ++ )
        if(opt[i] == 1)
            f = getlca(x[i] , y[i]) , printf("%d %d\n" , deep[x[i]] + deep[y[i]] - 2 * deep[f] + 1 , i - c[i] > 0 ? query(i - c[i] - 1 , 1 , m , root[x[i]] , root[y[i]] , root[f] , root[fa[f][0]]) : 0);
    return 0;
}

 

 

posted @ 2017-06-06 10:30  GXZlegend  阅读(310)  评论(0编辑  收藏  举报