BZOJ 4771 七彩树

主席树+差分

如果没有深度限制的话,显然有一种维护方式:对于每一种点,用差分来搞,给自己+1,给同一颜色的前驱和自己的LCA-1,给同一颜色的后继和自己的LCA-1,然后查子树和。

加了深度限制就坑爹了。直接用dfs序维护不了,因为询问区间不连续。

考虑每一个询问,我们只希望所有深度不超过dep[x]+d的点有贡献,希望上述的维护只针对前几层的点。因此我们考虑按层来预处理信息。然后用一个能资瓷加和查的数据结构。

得到算法:对于每一层,按dfs序维护一个主席树,维护前几层的信息和。询问时只需查询某一层的主席树上dfs在某一区间的和即可。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<set>
#define N 100005
using namespace std;
namespace runzhe2000
{
    int read()
    {
        int r = 0; char c = getchar();
        for(; c < '0' || c > '9'; c = getchar());
        for(; c >='0' && c <='9'; r = r*10+c-'0', c = getchar());
        return r;
    }
    int n, m, last[N], ecnt, c[N], q[N];
    int beg[N], end[N], timer, dep[N], siz[N], fa[N], son[N], top[N];
    struct Pair
    {
        int fir, sec;
        bool operator < (const Pair &that) const {if(fir != that.fir) return fir < that.fir; else return sec < that.sec;}
    };
    struct edge{int next, to;}e[N];
    void addedge(int a, int b){e[++ecnt]=(edge){last[a],b};last[a]=ecnt;}
    void dfs1(int x)
    {
        x == 1 ? dep[x] = 0 : dep[x] = dep[fa[x]] + 1; siz[x] = 1; beg[x] = ++timer;
        for(int i = last[x]; i; i = e[i].next)
        {
            int y = e[i].to; fa[y] = x; dfs1(y); siz[x] += siz[y]; 
            siz[y] > siz[son[x]] ? son[x] = y : 0;
        } 
        end[x] = timer;
    }
    void dfs2(int x)
    {
        top[x] = son[fa[x]]==x ? top[fa[x]] : x;
        for(int i = last[x]; i; i = e[i].next) dfs2(e[i].to);
    }
    int get_lca(int a, int b)
    {
        for(; top[a] != top[b]; ) dep[top[a]] < dep[top[b]] ? b = fa[top[b]] : a = fa[top[a]]; 
        return dep[a] < dep[b] ? a : b;
    }
    struct seg
    {
        seg *ls, *rs;
        int sum;
    }mem[N*200], *root[N]; int mem_tot;
    seg* newnode(int sum = 0){return &(mem[mem_tot++] = (seg){NULL, NULL, sum});}
    void pushup(seg *p){p->sum = 0; p->ls ? p->sum += p->ls->sum : 0; p->rs ? p->sum += p->rs->sum : 0; }
    void modi(seg *&x, int l, int r, int p, int v)
    {
        x == NULL ? x = newnode() : 0;
        if(l == r) {x->sum += v; return;}   int mid = (l+r)>>1;
        p <= mid ? modi(x->ls, l, mid, p, v) : modi(x->rs, mid+1, r, p, v);
        pushup(x);
    }
    set<Pair> s[N];
    set<Pair>::iterator it1, it2;
    seg *merge(seg *x, seg *y) // not modify x
    {
        if(!x || !y) return x ? x : y;
        y->sum += x->sum; 
        y->ls = merge(x->ls, y->ls);
        y->rs = merge(x->rs, y->rs);
        return y;
    }
    int query(seg *x, int l, int r, int ql, int qr)
    {
        if(x == NULL) return 0; if(ql <= l && r <= qr) return x->sum;
        int mid = (l+r)>>1, ret = 0;
        if(ql <= mid) ret += query(x->ls, l, mid, ql, qr);
        if(mid <  qr) ret += query(x->rs, mid+1, r, ql, qr);
        return ret;
    }
    void clr()
    {
        ecnt = timer = mem_tot = 0;
        memset(last, 0, sizeof(last)); 
        memset(son, 0, sizeof(son)); 
        memset(q, 0, sizeof(son));
    }
    void main()
    {
        for(int T = read(); T--; )
        {
            n = read(); m = read(); clr();
            for(int i = 1; i <= n; i++) c[i] = read(), s[c[i]].clear();
            for(int i = 2; i <= n; i++) addedge(read(), i);
            dfs1(1); dfs2(1); q[0] = 1; 
            int cur = 0; root[0] = newnode();
            for(int head=0,tail=1; head<tail; head++)
            {
                int x = q[head], col = c[x]; bool havel, haver;
                for(int i = last[x]; i; i = e[i].next) q[tail++] = e[i].to;
                modi(root[cur], 1, n, beg[x], 1);
                it1 = s[col].lower_bound((Pair){beg[x], x});
                it1 == s[col].begin() ? havel = 0 : (--it1, havel = 1);
                it2 = s[col].upper_bound((Pair){beg[x], x});
                it2 == s[col].end() ? haver = 0 : haver = 1;
                s[col].insert((Pair){beg[x],x});
                if(havel) modi(root[cur], 1, n, beg[get_lca(it1->sec, x)], -1);
                if(haver) modi(root[cur], 1, n, beg[get_lca(it2->sec, x)], -1);
                if(havel && haver) modi(root[cur], 1, n, beg[get_lca(it1->sec, it2->sec)], 1);
                if(dep[q[head+1]] != dep[x])
                {
                    if(cur) root[cur] = merge(root[cur-1], root[cur]);
                    root[++cur] = newnode();
                }
            }
            int max_dep = dep[q[n-1]];
            for(int ans = 0, x, d; m--; )
            {
                x = read()^ans; d = read()^ans;
                d = min(dep[x] + d, max_dep);
                printf("%d\n",ans = query(root[d],1,n,beg[x],end[x]));
            }
        }
    }
}
int main()
{
    runzhe2000::main();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值