[BZOJ3052/UOJ#58][WC2013]糖果公园(带修改的树上莫队)

Address

https://www.lydsy.com/JudgeOnline/problem.php?id=3052
http://uoj.ac/problem/58

Solution

作为裸题,介绍一下带修改莫队树上莫队
我们知道,一般的莫队是不兹磁修改的。
但如果加入询问时间这一维,那么莫队就能支持一些简单的修改。
这里我们对一个询问的时间 t 的定义是:在这次询问之前,离这次询问最近的修改的编号(第 i 次修改的编号为 i )。
考虑如何从一个询问 ( l , r , t ) (查询区间 [ l , r ] ,上一次修改的编号为 t )转移到询问 ( l 0 , r 0 , t 0 )
假设当前在 ( l , r , t ) ,即当前记录的是序列的区间 [ l , r ] 内,第 t 次修改之后的状态。
区间 [ l , r ] 转移到区间 [ l 0 , r 0 ] 比较容易,主要是由第 t 次修改转移到第 t 0 次修改。
如果 t < t 0 ,那么就按顺序对于每个 i ( t , t 0 ] ,执行编号为 i 的修改,如果第 i 次修改发生在区间 [ l , r ] 内则要为答案贡献。
如果 t > t 0 ,那么就倒着对于每个 i ( t 0 , t ] ,从第 i 次修改之后的状态回到第 i 1 次修改之后的状态,同样地,如果第 i 次修改发生在区间 [ l , r ] 则要为答案贡献。故对于每一次单点修改 ( i , x ) (即将第 i 个数修改成 x ),需要记录修改之前第 i 个数的值。
如何将询问排序:将序列分块,每 S 个数为一块,最后不足的为一块。
把询问 ( l , r , t ) 按照 l 所在块为第一关键字, r 所在块为第二关键字, t 为第三关键字排序。
可以分析得到复杂度 O ( ( n S ) 2 × n + S × n )
S = n 2 3 时有最优复杂度 O ( n 5 3 )
介绍树上莫队。
莫队询问的是序列上的区间,而树上莫队询问的是树上的路径。
考虑从路径 ( u , v ) 转移到 ( u 0 , v 0 )
假设当前在 ( u , v ) ,即当前记录的是 u v 的路径上( lca 除外)的信息,那么从 ( u , v ) 转移到 ( u 0 , v 0 ) ,就只需要将 u u 0 的路径上的所有点( lca 除外)的状态(即是否在莫队当前讨论的点集内)取反,再将 v v 0 的路径上的所有点的( lca 除外)的状态取反。这样,莫队当前讨论的点集就变成了 u 0 v 0 的路径( lca 除外)上的所有点。
简要说明: u v 的路径上的所有点( lca 除外)组成的点集,就是 u 到根的路径与 v 到根的路径的异或。根据异或运算的交换律和结合律,可以得到, u v 的路径( lca 除外)的所有点异或上 u u 0 的路径( lca 除外)再异或上 v v 0 的路径( lca 除外),就得到 u 0 v 0 的路径( lca 除外)。
对于询问排序,可以树分块之后按照 u 的所在块为第一关键字, v 的 DFS 序为第二关键字排序。
树分块就是在对树 DFS 退栈的时候,如果退栈的元素个数大于或等于给定的块大小,那么就把这些点分进一个块。详见 BZOJ 1086 。
回到此题。
此题是一个带修改的树上莫队,询问求的是:

i = 1 m v i j = 1 c n t [ i ] w j

c n t [ i ] 为种类为 i 的节点数。
带修改的树上莫队,只需要在树上莫队加入时间维即可实现。
取块大小为 n 2 3 时复杂度 O ( n 5 3 )

Code

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
#define Rof(i, a, b) for (i = a; i >= b; i--)
#define Tree(u) for (int e = adj[u], v; e; e = nxt[e]) if ((v = go[e]) != fu)
using namespace std;

inline int read()
{
    int res = 0; bool bo = 0; char c;
    while (((c = getchar()) < '0' || c > '9') && c != '-');
    if (c == '-') bo = 1; else res = c - 48;
    while ((c = getchar()) >= '0' && c <= '9')
        res = (res << 3) + (res << 1) + (c - 48);
    return bo ? ~res + 1 : res;
}

typedef long long ll;
const int N = 1e5 + 5, M = N << 1, LogN = 18;
int n, m, q, V[N], W[N], ecnt, nxt[M], adj[N], go[M], C[N], nque, ncha,
pos[N], fr[N], to[N], tmp[N], S, top, stk[N], dep[N], bcnt, bel[N],
_u = 1, _v = 1, _t, cnt[N], fa[N][LogN];
ll res, ans[N];
bool vis[N];

void add_edge(int u, int v)
{
    nxt[++ecnt] = adj[u]; adj[u] = ecnt; go[ecnt] = v;
    nxt[++ecnt] = adj[v]; adj[v] = ecnt; go[ecnt] = u;
}

void dfs(int u, int fu)
{
    int i;
    fa[u][0] = fu;
    For (i, 0, 15) fa[u][i + 1] = fa[fa[u][i]][i];
    dep[u] = dep[fu] + 1;
    int mp = top;
    Tree(u)
    {
        dfs(v, u);
        if (top - mp >= S)
        {
            bcnt++;
            while (top > mp) bel[stk[top--]] = bcnt;
        }
    }
    stk[++top] = u;
}

int lca(int u, int v)
{
    int i;
    if (dep[u] < dep[v]) swap(u, v);
    Rof (i, 16, 0)
    {
        if (dep[fa[u][i]] >= dep[v]) u = fa[u][i];
        if (u == v) return u;
    }
    Rof (i, 16, 0)
        if (fa[u][i] != fa[v][i])
            u = fa[u][i], v = fa[v][i];
    return fa[u][0];
}

struct cyx
{
    int u, v, t, bu, bv, id;
} que[N];

bool comp(cyx a, cyx b)
{
    return a.bu < b.bu || (a.bu == b.bu && (a.bv < b.bv
        || (a.bv == b.bv && a.t < b.t)));
}

void invt(int u)
{
    if (vis[u]) res -= 1ll * V[C[u]] * W[cnt[C[u]]--], vis[u] = 0;
    else res += 1ll * V[C[u]] * W[++cnt[C[u]]], vis[u] = 1;
}

void rever(int u, int v)
{
    if (dep[u] < dep[v]) swap(u, v);
    while (dep[u] > dep[v])
        invt(u), u = fa[u][0];
    while (u != v)
        invt(u), invt(v), u = fa[u][0], v = fa[v][0];
}

void changein(int x)
{
    int bef = fr[x], now = to[x], u = pos[x];
    C[u] = now;
    if (vis[u])
    {
        res -= 1ll * V[bef] * W[cnt[bef]--];
        res += 1ll * V[now] * W[++cnt[now]];
    }
}

void changeout(int x)
{
    int bef = fr[x], now = to[x], u = pos[x];
    C[u] = bef;
    if (vis[u])
    {
        res -= 1ll * V[now] * W[cnt[now]--];
        res += 1ll * V[bef] * W[++cnt[bef]];
    }
}

int main()
{
    int i, typ, x, y;
    n = read(); m = read(); q = read();
    S = pow(n, 2.0 / 3.0);
    For (i, 1, m) V[i] = read();
    For (i, 1, n) W[i] = read();
    For (i, 1, n - 1) x = read(), y = read(),
        add_edge(x, y);
    For (i, 1, n) tmp[i] = C[i] = read();
    dfs(1, 0);
    while (top) bel[stk[top--]] = bcnt;
    while (q--)
    {
        typ = read();
        if (typ == 0)
        {
            x = read(); y = read();
            fr[++ncha] = tmp[x]; to[ncha] = y;
            pos[ncha] = x; tmp[x] = y;
        }
        else
        {
            x = read(); y = read();
            que[++nque] = (cyx) {x, y, ncha, bel[x], bel[y], nque};
        }
    }
    sort(que + 1, que + nque + 1, comp);
    For (i, 1, nque)
    {
        int tu = que[i].u, tv = que[i].v, tm = que[i].t;
        rever(_u, tu); _u = tu;
        rever(_v, tv); _v = tv;
        while (_t < tm) changein(++_t);
        while (_t > tm) changeout(_t--);
        int w = lca(tu, tv);
        ans[que[i].id] = res + 1ll * V[C[w]] * W[cnt[C[w]] + 1];
    }
    For (i, 1, nque) printf("%lld\n", ans[i]);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/xyz32768/article/details/82426451