dfs序+主席树 或者 树链剖分+主席树(没写) 或者 线段树套线段树 或者 线段树套splay 或者 线段树套树状数组 bzoj 4448

4448: [Scoi2015]情报传递

Time Limit: 20 Sec  Memory Limit: 256 MB
Submit: 588  Solved: 308
[Submit][Status][Discuss]

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

 

首先说明因为是寻找危险度大于C的,那么就相当于是往前面移动C天,因此我们就只需要寻找1~i-c-1就好了。然后也相当于每天都对某一个节点进行染色。当然,dfs序和树链剖分的最主要的思想还是要去重

 

思路一:dfs序+主席树,类似于bzoj 2588

每次都是按照:root[x] + root[y] - root[lca] - root[par[lca][0]]即可哦

 

//看看会不会爆int!数组会不会少了一维!
//取物问题一定要小心先手胜利的条件
#include <bits/stdc++.h>
using namespace std;
#pragma comment(linker,"/STACK:102400000,102400000")
#define LL long long
#define ALL(a) a.begin(), a.end()
#define pb push_back
#define mk make_pair
#define fi first
#define se second
#define haha printf("haha\n")
const int maxn = 2e5 + 100;
struct Tree{
    int lnub, rnub, sum;
    Tree(int l = 0, int r = 0, int s = 0): lnub(l), rnub(r), sum(s){}
}tree[maxn * 30];
int deep[maxn], par[maxn][30], root[maxn], day[maxn];
vector<int> G[maxn];
int n, m, tot;
int ty[maxn], x[maxn], y[maxn], c[maxn];
/*
由于每次询问都是询问危险值为c的,所以我们就要往前面走c+1天,表示在c+1天之前染色的数目。
我们可以发现,我们只要储存每次特工的开始的时间就好了。然后离线查询即可发现
*/

int update(int o, int l, int r, int pos){
    int k = ++tot;
    tree[k] = tree[o]; tree[k].sum++;
    if (l == r && l == pos) return k;
    int mid = (l + r) / 2;
    if (pos <= mid) tree[k].lnub = update(tree[o].lnub, l, mid, pos);
    if (pos > mid) tree[k].rnub = update(tree[o].rnub, mid + 1, r, pos);
    return k;
}

void dfs(int u, int fa, int road){
    deep[u] = road; par[u][0] = fa;
    root[u] = root[fa];
    if (day[u]) root[u] = update(root[fa], 1, m, day[u]);
    for (int i = 1; i <= 20; i++){
        if (road < (1 << i)) break;
        par[u][i] = par[par[u][i - 1]][i - 1];
    }
    for (int i = 0; i < G[u].size(); i++){
        int v = G[u][i];
        if (v == fa) continue;
        dfs(v, u, road + 1);
    }
}

int get_lca(int x, int y){
    if (deep[x] < deep[y]) swap(x, y);
    int delta = deep[x] - deep[y];
    for (int i = 0; i <= 20; i++){
        if (delta & (1 << i)) x = par[x][i];
    }
    if (x == y) return x;
    for (int i = 20; i >= 0; i--){
        if (par[x][i] != par[y][i])
            x = par[x][i], y = par[y][i];
    }
    return par[x][0];
}

int query(int o, int l, int r, int ql, int qr){
    if (ql > qr) return 0;
    if (ql <= l && qr >= r) return tree[o].sum;
    int ans = 0;
    int mid = (l + r) / 2;
    if (ql <= mid) ans += query(tree[o].lnub, l, mid, ql, qr);
    if (qr > mid) ans += query(tree[o].rnub, mid + 1, r, ql, qr);
    return ans;
}

void solve(){
    for (int i = 1; i <= m; i++){
        if (ty[i] == 1){
            int lca = get_lca(x[i], y[i]), old = par[lca][0];
            int ans = deep[x[i]] + deep[y[i]] - deep[lca] - deep[old];
            printf("%d ", ans);
            ans = 0;
            int k = i - c[i] - 1;
            ans += query(root[x[i]], 1, m, 1, k);
            ans += query(root[y[i]], 1, m, 1, k);
            ans -= query(root[lca], 1, m, 1, k);
            if (old) ans -= query(root[old], 1, m, 1, k);
            printf("%d\n", ans);
        }
    }
}

int main(){
    scanf("%d", &n);
    int rt;
    for (int i = 1; i <= n; i++){
        int fa; scanf("%d", &fa); int v = i;
        if (fa == 0){rt = i; continue;}
        G[fa].pb(v), G[v].pb(fa);
    }
    scanf("%d", &m);
    for (int i = 1; i <= m; i++){
        scanf("%d", ty + i);
        if(ty[i] == 1)
            scanf("%d%d%d", x + i, y + i, c + i);
        else {
            int T; scanf("%d", &T); day[T] = i;
        }
    }
    dfs(rt, 0, 1);
    solve();
    return 0;
}
View Code

 

思路二:树链剖分+主席树 (这玩意儿到现在没看懂,明天再弄)

主席树呢,是按照dfstime来对每个,root[i]保存的是目前dfstime的时间。然后所以每次询问呢,

都是query(root[pos[belong[x]] - 1]], root[pos[x]], 1, m, c)

 

思路三:线段树套线段树 = =还没看,在说吧

 

posted @ 2017-03-01 21:08  知る奇迹に  阅读(144)  评论(0编辑  收藏  举报