题目链接
思路:
网上题解都很多。。。 学习树上带修改莫队的蒟蒻一只。
大概想法就是, 跑出
dfs
d
f
s
序,这个
dfs
d
f
s
序进入每个点
x
x
的时候记录一下当前位置,每个点出来的时候也记录一下位置
rx
r
x
,然后对这个
dfs
d
f
s
序进行分块, 就和普通序列分块一样了。
树上普通莫队查询
x,y
x
,
y
之间的信息的话,如果
x,y
x
,
y
属于同一条链,假设
x
x
在前面出现,那么对应的查询区间就是
[lx,ly]
[
l
x
,
l
y
]
,不用担心在
x
x
和之间有其他子树存在,因为如果有, 那么也一定会对称遍历完, 我们可以用一个
vis
v
i
s
数组记录每个点访问的次数,访问了偶数次(可用异或操作)的话相当于没有访问,那么最终就只有
x
x
到的路径上的点存在了。对于
x,y
x
,
y
在不同链上, 那么询问区间就取
[rx,ly]
[
r
x
,
l
y
]
,同样如果
x,y
x
,
y
之间的子树也会对称遍历完,最终只剩下
x
x
到的简单路径, 注意这个时候
LCA
L
C
A
的信息没有加入进去,单独处理下
LCA
L
C
A
即可。
带修改也就是记录下第三个信息,模拟操作执行和撤销操作, 这部分暴力即可。
#include<bits/stdc++.h>
typedef long long ll;
const ll maxn = 2e5 + 10;
const ll block_size = 1610;
using namespace std;
struct opr {
ll id, u, v, tim, lca;
opr() {}
opr(ll id, ll u, ll v, ll tim, ll lca) :
id(id), u(u), v(v), tim(tim), lca(lca) {}
bool operator < (opr p) const {
ll bu = u / block_size, pu = p.u / block_size;
ll bv = v / block_size, pv = p.v / block_size;
if(bu != pu) return bu < pu;
if(bv != pv) return bv < pv;
return tim < p.tim;
}
} rec[maxn];
ll n, m, q, vis[maxn];
vector<ll> G[maxn];
ll anc[maxn][18], deep[maxn], tot[maxn];
ll ans[maxn], v[maxn], w[maxn], sum[maxn], now_ans;
ll l[maxn], r[maxn], dfn[maxn], num, c[maxn];
ll id[maxn], last[maxn], now[maxn], res[maxn];
void dfs(ll x, ll fa, ll d) {
l[x] = num; dfn[num++] = x;
anc[x][0] = fa; deep[x] = d;
for(ll i = 1; i < 18; i++) {
ll t = anc[x][i - 1];
if(~t) anc[x][i] = anc[t][i - 1];
}
for(ll i = 0; i < G[x].size(); i++) {
ll v = G[x][i];
if(v == fa) continue;
dfs(v, x, d + 1);
}
dfn[num++] = x;
r[x] = num - 1;
}
ll lca(ll x, ll y) {
if(deep[x] < deep[y]) swap(x, y);
for(ll lg = 17; lg >= 0; lg--) {
if(deep[x] - (1 << lg) < deep[y]) continue;
x = anc[x][lg];
if(deep[x] == deep[y]) break;
}
if(x == y) return x;
for(ll lg = 17; lg >= 0; lg--) {
if(anc[x][lg] == anc[y][lg]) continue;
x = anc[x][lg]; y = anc[y][lg];
}
return anc[x][0];
}
void solve_poll(ll x) {
vis[x] ^= 1;
ll co = c[x];
if(vis[x]) { tot[c[x]]++; now_ans += (sum[tot[co]] - sum[tot[co] - 1]) * v[co]; }
else { tot[co]--; now_ans += (sum[tot[co]] - sum[tot[co] + 1]) * v[co]; }
}
void solve(ll now_id, ll las_l, ll las_r, ll las_t, ll now_l, ll now_r, ll now_t) {
while(las_t < now_t) {
las_t++;
ll u = id[las_t], od = last[las_t], nw = now[las_t];
if(vis[u]) {
now_ans += (sum[tot[od] - 1] - sum[tot[od]]) * v[od];
now_ans += (sum[tot[nw] + 1] - sum[tot[nw]]) * v[nw];
tot[od]--; tot[nw]++;
}
c[u] = nw;
}
while(las_t > now_t) {
ll u = id[las_t], od = now[las_t], nw = last[las_t];
if(vis[u]) {
now_ans += (sum[tot[od] - 1] - sum[tot[od]]) * v[od];
now_ans += (sum[tot[nw] + 1] - sum[tot[nw]]) * v[nw];
tot[od]--; tot[nw]++;
}
c[u] = nw; las_t--;
}
while(las_l < now_l) { solve_poll(dfn[las_l]); las_l++; }
while(las_r > now_r) { solve_poll(dfn[las_r]); las_r--; }
while(las_l > now_l) { las_l--; solve_poll(dfn[las_l]); }
while(las_r < now_r) { las_r++; solve_poll(dfn[las_r]); }
ans[now_id] = now_ans;
}
const ll MAX = 10000;
char buf[MAX], *ps = buf, *pe = buf + 1;
inline void rnext() {
if(++ps == pe) pe = (ps = buf) + fread(buf, sizeof(char), sizeof(buf) / sizeof(char), stdin);
}
template <class T>
inline bool in(T &ans) {
ans = 0;
T f = 1;
if(ps == pe) return false;//EOF
do{
rnext();
if('-' == *ps) f = -1;
} while(!isdigit(*ps) && ps != pe);
if(ps == pe) return false;//EOF
do {
ans = (ans<<1)+(ans<<3)+*ps-48;
rnext();
} while(isdigit(*ps) && ps != pe);
ans *= f;
return true;
}
int main() {
while(in(n)) {
in(m); in(q);
ll number = 0; num = 1;
now_ans = 0;
memset(vis, 0, sizeof vis);
for(ll i = 0; i < maxn; i++) {
G[i].clear();
ans[i] = -1;
tot[i] = vis[i] = 0;
for(ll j = 0; j < 18; j++) anc[i][j] = -1;
}
for(ll i = 1; i <= m; i++) in(v[i]);
sum[0] = 0;
for(ll i = 1; i <= n; i++) { in(w[i]); sum[i] = sum[i - 1] + w[i]; }
for(ll i = 1; i < n; i++) {
ll u, v; in(u); in(v);
G[u].push_back(v);
G[v].push_back(u);
}
for(ll i = 1; i <= n; i++) { in(c[i]); res[i] = c[i]; }
dfs(1, -1, 1);
ll now_time = 0;
for(ll i = 0; i < q; i++) {
ll op, u, v;
in(op); in(u); in(v);
if(op == 1) {
if(l[u] > l[v]) swap(u, v);
ll LCA = lca(u, v);
if(LCA == u || LCA == v) rec[number++] = opr(i, l[u], l[v], now_time, LCA);
else rec[number++] = opr(i, r[u], l[v], now_time, LCA);
} else {
now_time++; id[now_time] = u;
last[now_time] = c[u]; now[now_time] = v; c[u] = v;
}
}
sort(rec, rec + number);
now_ans = 0;
for(ll i = 1; i <= n; i++) c[i] = res[i];
ll las_l = 0, las_r = 0, las_t = 0;
for(ll i = 0; i < number; i++) {
ll now_l = rec[i].u, now_r = rec[i].v, LCA = rec[i].lca, idx = rec[i].id, n_t = rec[i].tim;
solve(rec[i].id, las_l, las_r, las_t, now_l, now_r, n_t);
if(LCA != dfn[now_l] && LCA != dfn[now_r]) {
ll col = c[LCA];
ans[rec[i].id] += (sum[tot[col] + 1] - sum[tot[col]]) * v[col];
}
las_l = now_l; las_r = now_r; las_t = n_t;
}
for(ll i = 0; i < q; i++) if(~ans[i]) printf("%lld\n", ans[i]);
}
return 0;
}
本文介绍了一种树上带修改的莫队算法实现,通过DFS序对树节点进行预处理,支持区间查询和更新操作。适用于解决复杂的数据结构问题。
491

被折叠的 条评论
为什么被折叠?



