[bzoj 3720] Gty的妹子树 (树上分块)

树上分块(块状树)


Description

我曾在弦歌之中听过你,
檀板声碎,半出折子戏。
舞榭歌台被风吹去,
岁月深处尚有余音一缕……
Gty神(xian)犇(chong)从来不缺妹子……
他来到了一棵妹子树下,发现每个妹子有一个美丽度……
由于Gty很哲♂学,他只对美丽度大于某个值的妹子感兴趣。
他想知道某个子树中美丽度大于k的妹子个数。
某个妹子的美丽度可能发生变化……
树上可能会出现一只新的妹子……
维护一棵初始有n个节点的有根树(根节点为1),树上节点编号为1-n,每个点有一个权值wi。
支持以下操作:
0 u x 询问以u为根的子树中,严格大于x的值的个数。(u^=lastans,x^=lastans)
1 u x 把u节点的权值改成x。(u^=lastans,x^=lastans)
2 u x 添加一个编号为"当前树中节点数+1"的节点,其父节点为u,其权值为x。(u^=lastans,x^=lastans)
最开始时lastans=0。

Input

输入第一行包括一个正整数n(1<=n<=30000),代表树上的初始节点数。
接下来n-1行,每行2个整数u,v,为树上的一条无向边。
任何时刻,树上的任何权值大于等于0,且两两不同。
接下来1行,包括n个整数wi,表示初始时每个节点的权值。
接下来1行,包括1个整数m(1<=m<=30000),表示操作总数。
接下来m行,每行包括三个整数 op,u,v:
op,u,v的含义见题目描述。
保证题目涉及的所有数在int内。

Output

对每个op=0,输出一行,包括一个整数,意义见题目描述。

Sample Input

2
1 2
10 20
1
0 1 5

Sample Output

2


Solution

这是一道很裸的树上分块题目(又叫块状树)。
我们设一个块的大小为M,对于一个节点,如果它的父亲所属块的大小小于M,则将该点加入其父亲的块,否则将该点加入一个新的块,并建一条从父亲块到新块的边。
在查询u节点为根的子树时,对于和u属于一个块的点暴力枚举,其他点则整块利用二分来计算。对于随机数据,时间复杂度可看做\(O(n * \sqrt{n} * log_n)\) ,可以接受。

Code

#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
#define adde(u, v) (e[ecnt] = (edge){v, head[u]}, head[u] = &e[ecnt++])
#define addbe(u, v) (be[blcnt] = (edge){v, bhead[u]}, bhead[u] = &be[blcnt++])

const int maxn = 3e5 + 10, Block = 200;
int ecnt, bcnt, blcnt;
int w[maxn<<1], fa[maxn<<1], bel[maxn];
struct edge {int v; edge *next;} e[maxn<<2], *head[maxn<<1], be[maxn<<1], *bhead[maxn];
struct BLOCK {
    int sz, a[Block]; 
    void sort() {std::sort(a, a + sz);}
    int find(int k) {return sz - (upper_bound(a, a + sz, k) - a);}
} B[40000];

void build(int u) {
    for(edge *k = head[u]; k; k = k->next) if(fa[u] != k->v) {
        int tmp;
        if(B[bel[u]].sz < Block) {
            tmp = bel[k->v] = bel[u];
            B[tmp].a[B[tmp].sz++] = w[k->v];
        }
        else {
            tmp = bel[k->v] = ++bcnt;
            B[bcnt].a[B[bcnt].sz++] = w[k->v];
            addbe(bel[u], tmp);
        }
        B[tmp].sort();
        fa[k->v] = u;
        build(k->v);
    }
}

void update(int u, int v) {
    int tmp = bel[u], k = lower_bound(B[tmp].a, B[tmp].a + B[tmp].sz, w[u]) - B[tmp].a;
    B[tmp].a[k] = v; w[u] = v;
    B[tmp].sort();
}


int bdfs(int u, int x) {
    int res = B[u].find(x);
    for(edge *k = bhead[u]; k; k = k->next) {
        res += bdfs(k->v, x);
    }
    return res;
}

int pdfs(int u, int x) {
    int res = 0;
    if(w[u] > x) res++; 
    for(edge *k = head[u]; k; k = k->next) if(k->v != fa[u]) {
        if(bel[u] == bel[k->v]) res += pdfs(k->v, x);
        else res += bdfs(bel[k->v], x);
    }
    return res;
}

int query(int u, int k) {
    int p, res = 0; 
    res += pdfs(u, k);
    return res;
}

int main() {
    int n, m, u, v, last = 0, op;
    scanf("%d", &n);
    for(int i = 1; i < n; i++) scanf("%d%d", &u, &v), adde(u, v), adde(v, u);
    for(int i = 1; i <= n; i++) scanf("%d", &w[i]);
    fa[1] = -1, B[++bcnt].sz = 1, B[bcnt].a[0] = w[1], bel[1] = bcnt;
    build(1);
    scanf("%d", &m);
    for(int i = 0; i < m; i++) {
        scanf("%d%d%d", &op, &u, &v); u ^= last, v ^= last;
        if(op == 0) {
            printf("%d\n", last = query(u, v));
        }
        if(op == 1) {
            update(u, v);
        }
        if(op == 2) {
            w[++n] = v;
            adde(u, n);
            fa[n] = u;
            int tmp;
            if(B[tmp = bel[u]].sz < Block) B[tmp].a[B[tmp].sz++] = v, bel[n] = bel[u];
            else B[tmp = ++bcnt].a[B[tmp].sz++] = v, addbe(bel[u], bcnt), bel[n] = bcnt;
            B[tmp].sort();
        }
    }
    return 0;
 }

转载于:https://www.cnblogs.com/ZegWe/p/6496946.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值