BZOJ 4771: 七彩树

关于维护每个节点子树里某些东西的手段有:转化为dfs序的一个区间、dsu on tree(树分块?树上莫队?)
还有一个就是线段树合并
这个题就很nb
对每个节点开两个权值线段树
第一个线段树维护每个深度出现的颜色种类数,每种颜色只在最浅深度起贡献
第二个线段树维护每种颜色出现的最浅深度
维护方法就是,首先进入一个节点,对第一棵线段树的 \(dep_u\) 设为 \(1\),对第二棵线段树的 \(c_u\) 设为 \(dep_u\)
然后dfs叶子节点,回溯之后进行合并,先合并第一棵线段树,发现,每种颜色最多会重复一次,那么在合并第二棵线段树的时候,到了叶子节点,如果 \(u\)\(v\) 对应节点都有值,那么保留较小值,然后把 \(u\) 的第一棵线段树的较大深度的值 \(-1\) 即可
查询就是在第一棵线段树上二维数点
要可持久化一下,可持久化大概就是修改每个节点都要新建一个节点,保留原来节点的信息
复杂度 \(O(n \log n)\)
还是比较好写的
要记得线段树合并这个强大的东西啊啊啊啊

#include 
#define lp tree[p].l
#define rp tree[p].r
#define mid ((l + r) >> 1)
#define lq tree[q].l
#define rq tree[q].r

namespace IO {
    char buf[1 << 21], *p1 = buf, *p2 = buf;
    inline void read() {}
    inline int getc() {
      return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++;
    }
    template
    inline void read(T &x, T2 &... oth) {
        T f = 1; x = 0; char ch = getc();
        while (!isdigit(ch)) { if (ch == '-') f = -1; ch = getc(); }
        while (isdigit(ch)) x = x * 10 + ch - 48, ch = getc(); 
        x *= f;
        read(oth...);
    }
}

const int N = 1e5 + 7;
const int INF = 0x3f3f3f3f;
int c[N], head[N], cnt = 1, to[N], ne[N];
int n, m, root1[N], root2[N], tol, dep[N];
inline void add(int u, int v) {
    to[++cnt] = v; ne[cnt] = head[u]; head[u] = cnt;
}

struct Tree {
    int l, r, v;
    void clear() {
        l = r = v = 0;
    }
} tree[N * 100];

inline int newnode() {
    int p = ++tol;
    tree[p].clear();
    return p;
}

void update(int &p, int q, int l, int r, int pos, int v) {
    tree[p = newnode()] = tree[q];
    tree[p].v += v;
    if (l == r) return;
    if (pos <= mid) update(lp, lq, l, mid, pos, v);
    else update(rp, rq, mid + 1, r, pos, v);
}

int merge1(int p, int q, int l, int r) {
    if (!p || !q) return p | q;
    int cur = newnode();
    tree[cur].v = tree[p].v + tree[q].v;
    if (l == r) return cur;
    tree[cur].l = merge1(lp, lq, l, mid);
    tree[cur].r = merge1(rp, rq, mid + 1, r);
    return cur;
}

int merge2(int p, int q, int l, int r, int &rt) {
    if (!p || !q) return p | q;
    int cur = newnode();
    tree[cur].v = INF;
    if (l == r) {
        tree[cur].v = std::min(tree[p].v, tree[q].v);
        int mx = std::max(tree[p].v, tree[q].v);
        if (mx != INF) update(rt, rt, 1, n, mx, -1);
        return cur;
    }
    tree[cur].l = merge2(lp, lq, l, mid, rt);
    tree[cur].r = merge2(rp, rq, mid + 1, r, rt);
    return cur;
}

int query(int p, int l, int r, int x, int y) {
    if (!p) return 0;
    if (x <= l && y >= r) return tree[p].v;
    int ans = 0;
    if (x <= mid) ans += query(lp, l, mid, x, y);
    if (y > mid) ans += query(rp, mid + 1, r, x, y);
    return ans;
}

void dfs(int u, int f) {
    dep[u] = dep[f] + 1;
    update(root1[u], root1[u], 1, n, dep[u], 1);
    update(root2[u], root2[u], 1, n, c[u], dep[u]);
    for (int i = head[u]; i; i = ne[i]) {
        int v = to[i];
        dfs(v, u);
        root1[u] = merge1(root1[u], root1[v], 1, n);
        root2[u] = merge2(root2[u], root2[v], 1, n, root1[u]);
    }
}

void solve() {
    IO::read(n, m);
    for (int i = 1; i <= n; i++) 
        IO::read(c[i]);
    for (int i = 2; i <= n; i++) {
        int f;
        IO::read(f);
        add(f, i);
    }
    dfs(1, 0);
    int ans = 0;
    for (int u, d; m--; ) {
        IO::read(u, d);
        u ^= ans, d ^= ans;
        printf("%d\n", ans = query(root1[u], 1, n, dep[u], dep[u] + d));
    }
}

void init() {
    cnt = 1;
    tol = 0;
    for (int i = 1; i <= n; i++)
        head[i] = root1[i] = root2[i] = dep[i] = 0;
}

int main() {
    int T;
    IO::read(T);
    while (T--) {
        solve();
        if (T) init();
    }
    return 0;
}

你可能感兴趣的:(BZOJ 4771: 七彩树)