BZOJ 4448: [Scoi2015]情报传递 树剖套主席树

来源:互联网 发布:闹钟铃声推荐 知乎 编辑:程序博客网 时间:2024/05/16 12:41

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

Input
第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,表示搜集情报的
情报员编号。

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

Sample Input
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

Sample Output
5 0

5 2

5 1
HINT

对于3个传递情报任务,都是经过5名情报员,分别是4号、2号、1号、3号和7号。其中,对于第1个

任务,所有情报员(危险值为0)都不对情报构成威胁;对于第2个任务,有2名情报员对情报构成威胁,

分别是1号情报员(危险值为3)和4号情报员(危险值为2),7号情报员(危险值为1)并不构成威胁;

对于第3个任务,只有1名情报员对情报构成威胁。

n< = 2×10^5,Q< = 2×105,0< Pi,C!< = N, 1< = Ti,Xi,Yi< = n

解题方法: 首先是有一种树套树的方法,但是蒟蒻根本不会,只好请教网上的大神博客了。看到一种树剖加主席树的做法,必须果断A掉,具体来说是这样的。我们考虑离线之后把所有的2操作全部更新之后,1操作就变成了查询一条链上有多少个小于等于i-c[i]-1的值,这显然主席树裸题啦。不过是在树上操作,所以我们树剖一下就好了。
复杂度: O(nlognlogn)

//bzoj 4448 树剖套主席树#include <bits/stdc++.h>using namespace std;const int maxn = 2e5+7;const int maxm = maxn*30;//=========================================================================================树剖int n, m, cnt, cnt2, sz, edgecnt;int dep[maxn], siz[maxn], head[maxn], fa[maxn], pos[maxn], bl[maxn], t[maxn], pos2[maxn];struct edge{int v, nxt; } E[maxn*2];void init(){memset(head, -1, sizeof(head)); edgecnt = 0; cnt2 = 0;}void addedge(int u, int v){E[edgecnt].v = v, E[edgecnt].nxt = head[u], head[u] = edgecnt++;}void dfs1(int x){    siz[x] = 1;    for(int i = head[x]; ~i; i = E[i].nxt){        if(E[i].v == fa[x]) continue;        dep[E[i].v] = dep[x] + 1;        fa[E[i].v] = x;        dfs1(E[i].v);        siz[x] += siz[E[i].v];    }}void dfs2(int x, int chain){    int k = 0; sz++;    pos[x] = sz; //分配x节点在线段树中的编号    bl[x] = chain; //记录链的顶端    pos2[sz] = x; //线段树中某一个编号对应的节点    for(int i = head[x]; ~i; i = E[i].nxt){        if(dep[E[i].v] > dep[x] && siz[E[i].v] > siz[k]){            k = E[i].v; //选择子树最大的儿子继承重链        }    }    if(k == 0) return;    dfs2(k, chain);    for(int i = head[x]; ~i; i = E[i].nxt){        if(dep[E[i].v] > dep[x] && k != E[i].v){            dfs2(E[i].v, E[i].v); //其余儿子新开重链        }    }}//===================================================================================主席树int T[maxm], lson[maxm], rson[maxm], c[maxm], tot;int build(int l, int r){    int root = tot++;    c[root] = 0;    if(l != r){        int mid = (l + r) / 2;        lson[root] = build(l, mid);        rson[root] = build(mid + 1, r);    }    return root;}int update(int root, int pos, int val){    int newroot = tot++, tmp = newroot;    c[newroot] = c[root] + val;    int l = 1, r = m;    while(l < r){        int mid = (l + r)/2;        if(pos <= mid){            lson[newroot] = tot++, rson[newroot] = rson[root];            newroot = lson[newroot], root = lson[root], r = mid;        }        else{            rson[newroot] = tot++, lson[newroot] = lson[root];            newroot = rson[newroot], root = rson[root], l = mid + 1;        }        c[newroot] = c[root] + val;    }    return tmp;}int query(int L, int R, int k){    int res = 0;    int l = 1, r = m;    while(l < r){        int mid = (l + r) / 2;        if(k <= mid){            r = mid;            L = lson[L];            R = lson[R];        }        else{            l = mid + 1;            res += c[lson[R]] - c[lson[L]];            L = rson[L];            R = rson[R];        }    }    return res + (k < l ? 0 : c[R] - c[L]);}//=================================================================================树剖查询struct Q{    int x, y, c;    Q(){}    Q(int x, int y, int c) : x(x), y(y), c(c) {}}q[maxn];void solve(int x, int y, int c){    int ans = 0;    int tmp = dep[x] + dep[y];    while(bl[x] != bl[y]){        if(dep[bl[x]] < dep[bl[y]]) swap(x, y);        ans += query(T[pos[bl[x]] - 1], T[pos[x]], c);        x = fa[bl[x]];    }    if(pos[x] > pos[y]) swap(x, y);    ans += query(T[pos[x] - 1], T[pos[y]], c);    printf("%d %d\n", tmp - 2*dep[x] + 1, ans);}//====================================================================================int main(){    scanf("%d", &n);    init(); tot = 0;    for(int i = 1; i <= n; i++){        int x;        scanf("%d", &x);        if(x == 0) continue;        addedge(x, i);        addedge(i, x);    }    fa[1] = 1;    dfs1(1);    dfs2(1, 1);    scanf("%d", &m);    for(int i = 1; i <= n; i++) t[i] = m + 1;    for(int i = 1; i <= m; i++){        int op, x, y, c;        scanf("%d", &op);        if(op == 1){            scanf("%d%d%d", &x, &y, &c);            q[cnt2].x = x, q[cnt2].y = y, q[cnt2++].c = i - c - 1;        }        else{            scanf("%d", &x);            t[x] = i; //情报员编号为x的情报员在第i天收集情报        }    }    T[0] = build(1, m);    for(int i = 1; i <= n; i++) T[i] = update(T[i-1], t[pos2[i]], 1);    for(int i = 0; i < cnt2; i++) solve(q[i].x, q[i].y, q[i].c);    return 0;}
0 0
原创粉丝点击