Address
https://www.lydsy.com/JudgeOnline/problem.php?id=3052
http://uoj.ac/problem/58
Solution
作为裸题,介绍一下带修改莫队和树上莫队。
我们知道,一般的莫队是不兹磁修改的。
但如果加入询问时间这一维,那么莫队就能支持一些简单的修改。
这里我们对一个询问的时间
的定义是:在这次询问之前,离这次询问最近的修改的编号(第
次修改的编号为
)。
考虑如何从一个询问
(查询区间
,上一次修改的编号为
)转移到询问
。
假设当前在
,即当前记录的是序列的区间
内,第
次修改之后的状态。
区间
转移到区间
比较容易,主要是由第
次修改转移到第
次修改。
如果
,那么就按顺序对于每个
,执行编号为
的修改,如果第
次修改发生在区间
内则要为答案贡献。
如果
,那么就倒着对于每个
,从第
次修改之后的状态回到第
次修改之后的状态,同样地,如果第
次修改发生在区间
则要为答案贡献。故对于每一次单点修改
(即将第
个数修改成
),需要记录修改之前第
个数的值。
如何将询问排序:将序列分块,每
个数为一块,最后不足的为一块。
把询问
按照
所在块为第一关键字,
所在块为第二关键字,
为第三关键字排序。
可以分析得到复杂度
。
当
时有最优复杂度
。
介绍树上莫队。
莫队询问的是序列上的区间,而树上莫队询问的是树上的路径。
考虑从路径
转移到
。
假设当前在
,即当前记录的是
到
的路径上( lca 除外)的信息,那么从
转移到
,就只需要将
到
的路径上的所有点( lca 除外)的状态(即是否在莫队当前讨论的点集内)取反,再将
到
的路径上的所有点的( lca 除外)的状态取反。这样,莫队当前讨论的点集就变成了
到
的路径( lca 除外)上的所有点。
简要说明:
到
的路径上的所有点( lca 除外)组成的点集,就是
到根的路径与
到根的路径的异或。根据异或运算的交换律和结合律,可以得到,
到
的路径( lca 除外)的所有点异或上
到
的路径( lca 除外)再异或上
到
的路径( lca 除外),就得到
到
的路径( lca 除外)。
对于询问排序,可以树分块之后按照
的所在块为第一关键字,
的 DFS 序为第二关键字排序。
树分块就是在对树 DFS 退栈的时候,如果退栈的元素个数大于或等于给定的块大小,那么就把这些点分进一个块。详见 BZOJ 1086 。
回到此题。
此题是一个带修改的树上莫队,询问求的是:
为种类为 的节点数。
带修改的树上莫队,只需要在树上莫队加入时间维即可实现。
取块大小为 时复杂度 。
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;
}