BZOJ4771 七彩树

/*
线段树合并 维护每个颜色的最小深度即可
??? 要强制在线

考虑不限制深度的方法统计子树中的颜色个数将每种颜色按照dfs序排序,  然后树上每个点答案贡献 + 1, 同种颜色相邻dfs序的lca处-1
这样统计子树和即可

考虑假如有深度限制的话, 这个方法可以离线来做, 按照深度从小到大一层一层地加入点, 同时对于每种
颜色按照dfs序维护set, 那么对于所有的询问按照deep[x] + d排序就可以处理了

现在要求在线, 那么按照深度建立主席树, 每次在上面查询就好了

l 写成1 debug3小时 

*/
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<queue>
#include<cmath>
#include<set>
#define ll long long
#define M 200010
#define mmp make_pair
using namespace std;
int read() {
    int nm = 0, f = 1;
    char c = getchar();
    for(; !isdigit(c); c = getchar()) if(c == '-') f = -1;
    for(; isdigit(c); c = getchar()) nm = nm * 10 + c - '0';
    return nm * f;
}
int n, m, t, deep[M], root[M], sz[M], son[M], fa[M], top[M], dfn[M], low[M], dft, cor[M], maxdeep, ans, cnt;
vector<int> to[M], note[M];

struct cmp {
    bool operator () (const int &a, const int &b) const {
        return dfn[a] < dfn[b];
    }
};
set<int, cmp> st[M];
set<int>::iterator it, it2;
void init() {
    for(int i = 1; i <= n; i++) vector<int>().swap(to[i]), vector<int>().swap(note[i]), st[i].clear(), son[i] = 0;
    maxdeep = ans = cnt = dft = 0;
}

void dfs(int now, int f) {
    deep[now] = deep[f] + 1;
//  note[deep[now]].push_back(now);
    maxdeep = max(maxdeep, deep[now]);
    sz[now] = 1;
    for(int i = 0; i < to[now].size(); i++) {
        int vj = to[now][i];
        dfs(vj, now);
        if(sz[vj] > sz[son[now]]) son[now] = vj;
        sz[now] += sz[vj];
    }
}

void dfs(int now) {
    dfn[now] = ++dft;
    note[deep[now]].push_back(now);/*
    if(son[now]) {
        top[son[now]] = top[now];
        dfs(son[now]);
    }*/
    for(int i = to[now].size() - 1; i >= 0; i--) {
        int vj = to[now][i];
        if(vj == son[now]) top[vj] = top[now];
        else top[vj] = vj;
        dfs(vj);
    }
    low[now] = dft;
}

int lca(int a, int b) {
    while(top[a] != top[b]) {
        if(deep[top[a]] < deep[top[b]]) swap(a, b);
        a = fa[top[a]];
    }
    if(deep[a] > deep[b]) swap(a, b);
    return a;
}

int rt[M], ls[M * 50], rs[M * 50], v[M * 50];

void insert(int l, int r, int &now, int pos, int val) {
    if(!now) {
        now = ++cnt;
        ls[now] = rs[now] = v[now] = 0;
    }
    if(l == r) {
        v[now] += val;
        return;
    }
    int mid = (l + r) >> 1;
    if(pos <= mid) insert(l, mid, ls[now], pos, val);
    else insert(mid + 1, r, rs[now], pos, val);
    v[now] = v[ls[now]] + v[rs[now]];
}

int merge(int last, int now, int l, int r) {
    if(!last || !now) return last + now;
    if(l == r) {
        v[now] += v[last];
        return now;
    }
    int mid = (l + r) >> 1;
    ls[now] = merge(ls[last], ls[now], l, mid);
    rs[now] = merge(rs[last], rs[now], mid + 1, r);
    v[now] = v[ls[now]] + v[rs[now]];
    return now;
}

int query(int l, int r, int now, int ln, int rn) {
    if(l > rn || r < ln) return 0;
    if(l >= ln && r <= rn) return v[now]; 
    int mid = (l + r) >> 1;
    return query(l, mid, ls[now], ln, rn) + query(mid + 1, r, rs[now], ln, rn);
}

int main() {
//  freopen("1.in", "r", stdin);    freopen("2.out", "w", stdout);
    t = read();
    while(t--) {
        n = read(), m = read();
        init();
        for(int i = 1; i <= n; i++) cor[i] = read();
        for(int i = 2; i <= n; i++) fa[i] = read(), to[fa[i]].push_back(i);
        dfs(1, 0);
        top[1] = 1;
        dfs(1);
        rt[0] = 0;
        for(int d = 1; d <= maxdeep; d++) {
            rt[d] = 0;
            for(int i = note[d].size() - 1; i >= 0; i--) {
                int x = note[d][i];
                
            //  cout << x << " " << deep[x] << " " << dfn[x] << " " << cnt<< "\n";
                insert(1, n, rt[d], dfn[x], 1);
                it = st[cor[x]].lower_bound(x);
                int pre = -1, nxt = -1;
                if(it != st[cor[x]].end()) nxt = *it;
                if(it != st[cor[x]].begin()) pre = *(--it);
                if(pre != -1) insert(1, n, rt[d], dfn[lca(pre, x)], -1);
                if(nxt != -1) insert(1, n, rt[d], dfn[lca(x, nxt)], -1);
                if(pre != -1 && nxt != -1) insert(1, n, rt[d], dfn[lca(pre, nxt)], 1);
                st[cor[x]].insert(x);
            }
            rt[d] = merge(rt[d - 1], rt[d], 1, n);
        }
//      for(int i = 1; i < n; i++) cout << lca(i, i + 1) << " ";
    //  break;
        
        while(m--) {
            int x = read() ^ ans, d = read() ^ ans;
            d = min(maxdeep, deep[x] + d);
            ans = query(1, n, rt[d], dfn[x], low[x]);
            cout << ans << '\n';
        }
    }
    return 0;
}
/*
3
3 3
1 2 3
1 2
1 1 
2 2
3 3 
5 8
1 3 3 2 2
1 1 3 3
1 0
0 0
3 0
1 3
2 1
2 0
6 2
4 1

5 8
1 3 3 2 2
1 1 3 3
1 0
0 0
3 0
1 3
2 1
2 0
6 2
4 1
1
5 5
1 2 3 4 5
1 1 1 1
1 1 
1 1
0 0
1 1
5 5
*/

转载于:https://www.cnblogs.com/luoyibujue/p/10518344.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值