bzoj2589: Spoj 10707 Count on a tree II 树上按深度分块 可持久化块状链表

bzoj2589: Spoj 10707 Count on a tree II

Description

给定一棵N个节点的树,每个点有一个权值,对于M个询问(u,v),你需要回答u xor lastans和v这两个节点间有多少种不同的点权。其中lastans是上一个询问的答案,初始为0,即第一个询问的u是明文。

Input

第一行两个整数N,M。
第二行有N个整数,其中第i个整数表示点i的权值。
后面N-1行每行两个整数(x,y),表示点x到点y有一条边。
最后M行每行两个整数(u,v),表示一组询问。
数据范围是N<=40000 M<=100000 点权在int范围内

Output

M行,表示每个询问的答案。

Sample Input

8 2
105 2 9 3 8 5 7 7
1 2
1 3
1 4
3 5
3 6
3 7
4 8
2 5
3 8

Sample Output

4
4

HINT

暴力自重。。。

分析

一道树分块神题。

方法:深度分块

就是把树上隔根号n 的深度就分一块。
这样的分块方法不能保证块内点的个数不超过 n \sqrt n n 但是可以保证深度差与块个数不超过 n \sqrt n n 一般用于处理树上路径

思路

1.按深度分块,预处理每个点到块顶的答案。
若两点在同一块内,则可以直接暴力。
考虑不在块内的两点 u , v u, v u,v。并假设 u u u深度大。
可以直接采用 u u u的块顶 x x x v v v的答案。
这样剩下 u u u x x x n \sqrt n n 个节点的信息维护。
可以暴力考虑每个点
2.对于一个点,产生贡献当且仅当这个点的权值没有在 x , v x,v x,v的路径上出现过。
所以现在问题转化为,一个点权值是否在某条路径上出现过。这个问题可以用经典的主席树来做,但是单次询问复杂度是 O ( l o g n ) O(logn) O(logn) 的,无法通过

算法:可持久化块状链表

块状链表这个东西简单来讲就是把序列分成 n \sqrt n n 块,每块各自维护信息,并用链表的形式连接起来。
考虑可持久化这个东西。每一次改动仅仅会改动一个位置,也就是一个块上的信息。于是每进行一次修改,就暴力新建一个块,暴力继承之前的块,并修改那个点的信息。对于每个时间,记录下这个历史版本对应的块的编号。这样子的话,一次修改是 O ( n ) O(\sqrt n) O(n ),但是询问是 O ( 1 ) O(1) O(1)的。
回到这道题,每个节点用块状链表维护当前点到根某种权值最低的深度,暴力的时候判断当前权值的深度是否大于lca即可。

代码

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdlib>
const int M = 210, N = 4e4 + 10;
int ri() {
    char ch = getchar(); int x = 0, f = 1; for(;ch < '0' || ch > '9'; ch = getchar()) if(ch == '-') f  = -1;
    for(;ch >= '0' && ch <= '9'; ch = getchar()) x = (x << 1) + (x << 3) - '0' + ch; return x * f;
}
int p[N][M], be[N], b[N], a[N], l[N], q[N], fa[N], de[N], A[M][N], r[N];
int pr[N], c[N], nx[N << 1], to[N << 1], rt[M], tot, Sz, Bs, tp, nn, kd;
void add(int u, int v) {to[++tp] = v; nx[tp] = pr[u]; pr[u] = tp;}
void adds(int u, int v) {add(u, v); add(v, u);}
int F(int x) {
    int L = 1, R = nn, m;
    for(;L != R; l[m = L + R >> 1] >= x ? R = m : L = m + 1);
    return L;
}
struct B {
    int a[M];
    int &operator [] (int x) {return p[a[b[x]]][r[x]];}
    void Ins(B l, int x, int d) {
        int po = b[x], t = r[x];
        std::memcpy(a, l.a, sizeof(a));
        std::memcpy(p[++Sz], p[a[po]], sizeof(p[0]));
        p[Sz][t] = d; a[po] = Sz;
    }
}s[N];
struct {
    int sz[N], ds[N], d[N];
    void up(int u, int v) {sz[u] += sz[v]; if(sz[ds[u]] < sz[v]) ds[u] = v;}
    void Dfs(int u, int c) {
        d[u] = c; if(!ds[u]) return ; Dfs(ds[u], c);
        for(int i = pr[u]; i; i = nx[i])
        if(to[i] != fa[u] && to[i] != ds[u]) Dfs(to[i], to[i]);
    }
    int f(int u, int v) {
        for(;d[u] != d[v]; u = fa[d[u]])
            if(de[d[u]] < de[d[v]]) u ^= v ^= u ^= v;
        return de[u] < de[v] ? u : v;
    }
}lca;
int Dfs(int u, int f) {
    fa[u] = f; s[u].Ins(s[f], a[u], de[u] = de[f] + 1); lca.sz[u] = 1;
    q[++tp] = u; int md = de[u], p = tp;
    for(int i = pr[u]; i; i = nx[i]) if(to[i] != f)
        md = std::max(md, Dfs(to[i], u)), lca.up(u, to[i]);
    if(md - de[u] >= Bs || p == 1) {
        rt[++tot] = u;
        for(int i = p; i <= tp; ++i) be[q[i]] = tot;
        tp = p - 1; return de[u] - 1;
    }
    return md;
}
void Work(int u, int f, int *s) {
    if(!c[a[u]]++) ++kd; s[u] = kd;
    for(int i = pr[u]; i; i = nx[i]) if(to[i] != f) Work(to[i], u, s);
    if(!--c[a[u]]) --kd;
}
int Sa(int u, int v) {
    for(tp = kd = 0;u != v; u = fa[u]) {
        if(de[u] < de[v]) u ^= v ^= u ^= v;
        if(!c[q[++tp] = a[u]]) c[a[u]] = 1, ++kd;
    }
    for(kd += !c[a[u]];tp;) c[q[tp--]] = 0;
    return kd;
}
int Sb(int u, int v) {
    if(de[rt[be[u]]] < de[rt[be[v]]]) u ^= v ^= u ^= v;
    int w = rt[be[u]], d = de[lca.f(u, v)]; kd = A[be[u]][v];
    for(tp = 0;u != w; u = fa[u]) {
        if(!c[a[u]] && s[w][a[u]] < d && s[v][a[u]] < d)
            c[q[++tp] = a[u]] = 1, ++kd;
    }
    for(;tp;) c[q[tp--]] = 0;
    return kd;
}
int main() {
    int n = ri(), m = ri(); Bs = std::sqrt(n) - 1;
    for(int i = 1;i <= n; ++i) b[i] = (i - 1) / Bs + 1, r[i] = i % Bs;
    for(int i = 1;i <= n; ++i) l[i] = a[i] = ri();
    std::sort(l + 1, l + n + 1);
    l[nn = 1] =l[1]; for(int i = 2;i <= n; ++i) if(l[i] != l[i - 1]) l[++nn] = l[i];
    for(int i = 1;i <= n; ++i) a[i] = F(a[i]);
    for(int i = 1;i < n; ++i) adds(ri(), ri()); tp = 0;
    Dfs(1, 0); lca.Dfs(1, 1); 
    for(int i = 1;i <= tot; ++i) Work(rt[i], 0, A[i]);
    for(int la = 0;m--;) {
        int u = ri() ^ la, v = ri();
        printf("%d\n", la = be[u] == be[v] ? Sa(u, v) : Sb(u, v));
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值