「BZOJ3720」GTY的妹子树-时间分块+划分树

Description

维护一棵初始有 n n 个节点的有根树(根节点为 1 1 ),树上节点编号为 1n 1 − n ,每个点有一个权值 wi w i

支持以下操作:

0ux 0 u x 询问以 u u 为根的子树中,严格大于 x x 的值的个数。
1ux 1 u x u u 节点的权值改成 x x
2ux 2 u x 添加一个编号为”当前树中节点数+1”的节点,其父节点为 u u ,其权值为 x x

强制在线。

Solution

考虑对时间分块。

先用 dfs d f s 序+划分树(也可以主席树+离散化)预处理,然后开一个队列存储每次的修改。

对于一次询问,如果它是新加进来的点,那么直接在队列中查找对它有贡献的节点即可。否则先统计预处理的答案再考虑修改对询问的影响。

#include 
using namespace std;

const int maxn = 600005, maxw = (1ll << 31) - 2;

int n, m, w[maxn], lazy[maxn], S;
int que1[maxn], tot1, que2[maxn], tot2, vis[maxn];
int f[maxn][21], dep[maxn];
int dfn[maxn], low[maxn], Time, ord[maxn];

inline int gi()
{
    char c = getchar();
    while (c < '0' || c > '9') c = getchar();
    int sum = 0;
    while ('0' <= c && c <= '9') sum = sum * 10 + c - 48, c = getchar();
    return sum;
}

namespace segt
{

    vector<int> v[maxn];

    #define lch (s << 1)
    #define rch (s << 1 | 1)
    #define mid ((l + r) >> 1)

    void build(int s, int l, int r)
    {
        v[s].clear();
        if (l == r) {
            v[s].push_back(w[ord[l]]);
            return ;
        }
        build(lch, l, mid);
        build(rch, mid + 1, r);
        v[s].resize(r - l + 1);
        merge(v[lch].begin(), v[lch].end(), v[rch].begin(), v[rch].end(), v[s].begin());
    }

    int query(int s, int l, int r, int x, int y, int k)
    {
        if (x <= l && r <= y) 
            return v[s].end() - upper_bound(v[s].begin(), v[s].end(), k);
        int res = 0;
        if (x <= mid) res += query(lch, l, mid, x, y, k);
        if (y >= mid + 1) res += query(rch, mid + 1, r, x, y, k);
        return res;
    }

}

struct edge {
    int to, next;
} e[maxn * 2];
int h[maxn], tot;

inline void add(int u, int v)
{
    e[++tot] = (edge) {v, h[u]}; h[u] = tot;
    e[++tot] = (edge) {u, h[v]}; h[v] = tot;
}

void dfs(int u, int fa)
{
    ord[dfn[u] = ++Time] = u;
    for (int i = h[u], v; v = e[i].to, i; i = e[i].next)
        if (v != fa) dfs(v, u);
    low[u] = Time;
}

int tag;
void rebuild()
{
    Time = 0; ++tag; dfs(1, 0);
    segt::build(1, 1, n);
}

inline void getfa(int u, int fa)
{
    f[u][0] = fa; dep[u] = dep[fa] + 1;
    for (int i = 0; f[u][i]; ++i) f[u][i + 1] = f[f[u][i]][i];
}

inline bool subtree(int u, int v)
{
    int p = dep[v] - dep[u];
    for (int i = 20; i >= 0; --i)
        if (p & (1 << i)) v = f[v][i];
    return u == v;
}

void dfs2(int u, int fa)
{
    getfa(u, fa);
    for (int i = h[u], v; v = e[i].to, i; i = e[i].next)
        if (v != fa) dfs2(v, u);
}

int query(int u, int x)
{
    int res = 0;
    if (dfn[u]) 
        res = segt::query(1, dfn[1], low[1], dfn[u], low[u], x);
    for (int i = 1; i <= tot1; ++i)
        if (((lazy[que1[i]] > x) ^ (w[que1[i]] > x)) && subtree(u, que1[i])) {
            if (lazy[que1[i]] > x) ++res;
            else --res;
        }
    for (int i = 1; i <= tot2; ++i)
        if (w[que2[i]] > x && subtree(u, que2[i]))
            ++res;
    return res;
}

int main()
{
    freopen("gty.in", "r", stdin);
    freopen("gty.out", "w", stdout);

    n = gi();
    for (int i = 1; i < n; ++i) add(gi(), gi());
    for (int i = 1; i <= n; ++i) w[i] = gi();
    rebuild();
    dfs2(1, 0);

    m = gi(); S = ceil(sqrt(m) * 5);
    int opt, u, x, lastans = 0;
    while (m--) {
        opt = gi(); u = gi(); x = gi();
        u ^= lastans; x ^= lastans;
        if (x > maxw)
            return 1;
        if (opt == 0) printf("%d\n", lastans = query(u, x));
        else if (opt == 1) {
            if (vis[u] != tag) vis[u] = tag, que1[++tot1] = u;
            lazy[u] = x;
        } else {
            que2[++tot2] = ++n; getfa(n, u); add(u, n);
            w[n] = x; 
        }
        if (tot1 + tot2 >= S) {
            for (int i = 1; i <= tot1; ++i) w[que1[i]] = lazy[que1[i]];
            rebuild();
            tot1 = tot2 = 0;
        }
    }

    return 0;
}

你可能感兴趣的:(数据结构——线段树,算法——分块)