【Coel.学习笔记】莫队(下)- 树上莫队和二次离线莫队
作者:互联网
树上莫队和二次离线莫队都比较难,所以只讲几个模板(明明是你太懒了)。
树上莫队
我们之前处理的问题都是在数列上的,如果换成树,怎么办呢?下面这题给出了一个常用的方法。
SP10707 COT2 - Count on a tree II
洛谷传送门
给定一棵点带权的树,静态询问每两个节点之间(包括端点)路径上的不同权值数。
解析:这道题看起来和上一篇的普通莫队题很像,只不过从序列变成了树。
对于树上问题,有一种通法把它变成序列问题:构造一个欧拉序列。在深度优先遍历的时候,对于每个点到达时和离开时都记录下结果,这时每个节点都会出现两次。我们开设两个数组 \(fir_u\) 和 \(lst_u\),分别记录下 \(u\) 第一次和第二次出现的位置。对于端点 \(x,y\),如果 \(fir_x<fir_y\),判断 \(x,y\) 的最近公共祖先,若为 \(x\),则路径对应到欧拉序列上的区间为 \([fir_x,fir_y]\) 中只出现一次的数字,若不为 \(x\) 则路径对应到欧拉序列上的区间为 \([lst_x,fir_y]\) 中只出现一次的数字,以及 \(x,y\) 的最近公共祖先。经过这样麻烦至极的操作,我们把树上问题变成了序列问题。
但是这么做直接套莫队还是很麻烦,我们要对维护的数组微调一下。维护一个数组 \(vis_u\) 表示数字 \(u\) 在当前区间内出现次数,\(0\) 表示两次 \(1\) 表示一次。给莫队插入时,我们利用异或操作,给 \(vis_u\) 异或上 \(1\)。若异或后值为 \(0\),则相当于普通莫队的删除,反之为插入。显然插入一个数和删除一个数在这个定义下是等价的,所以插入和删除直接利用同一个函数即可。现在插入和删除均为 \(O(1)\),可以直接套莫队解决了。另外本题权值的值域很大,需要做离散化。
虽然看起来很模板,但使用到的算法相当多(离散化,欧拉序列,倍增求 LCA,莫队),还是比较难写的。当本题要求强制在线的时候,有一种名为“树分块”的扩展解法,在此不做详述,感兴趣的读者可以自行查阅资料。
#include <algorithm>
#include <cmath>
#include <cstring>
#include <iostream>
#include <queue>
#include <vector>
#define get(x) (x) / len
using namespace std;
const int maxn = 1e5 + 10;
int n, m, len, a[maxn];
int head[maxn], nxt[maxn], to[maxn], tot;
int dep[maxn], anc[maxn][20];
int euler[maxn], fir[maxn], lst[maxn], idx;
int cnt[maxn], ans[maxn];
bool vis[maxn];
vector<int> rec;
struct node {
int l, r, id, p;
node(int id = 0, int l = 0, int r = 0, int p = 0)
: l(l), r(r), id(id), p(p) {}
inline bool operator<(const node &x) const {
int i = get(l), j = get(x.l);
if (i != j) return i < j;
return r < x.r;
}
} q[maxn];
void add(int u, int v) { nxt[tot] = head[u], to[tot] = v, head[u] = tot++; }
inline void init_hash() {
sort(rec.begin(), rec.end());
rec.erase(unique(rec.begin(), rec.end()), rec.end());
for (int i = 1; i <= n; i++)
a[i] = lower_bound(rec.begin(), rec.end(), a[i]) - rec.begin();
}
void dfs_euler(int u, int fa) {
euler[++idx] = u;
fir[u] = idx;
for (int i = head[u]; ~i; i = nxt[i]) {
int v = to[i];
if (v != fa) dfs_euler(v, u);
}
euler[++idx] = u;
lst[u] = idx;
}
// void dfs_lca(int u, int fa) {
// for (int i = head[u]; ~i; i = nxt[i]) {
// int v = to[i];
// if (v == fa) continue;
// dep[v] = dep[u] + 1;
// anc[v][0] = u;
// dfs_lca(v, u);
// }
// if (u == 1 && fa == 0)
// for (int j = 1; j <= 15; j++)
// for (int i = 1; i <= n; i++) anc[i][j] = anc[anc[i][j - 1]][j -
// 1];
// }
void bfs_lca() {
queue<int> Q;
memset(dep, 0x3f, sizeof dep);
dep[0] = 0, dep[1] = 1;
Q.push(1);
while (!Q.empty()) {
int u = Q.front();
Q.pop();
for (int i = head[u]; ~i; i = nxt[i]) {
int v = to[i];
if (dep[v] > dep[u] + 1) {
dep[v] = dep[u] + 1;
anc[v][0] = u;
for (int k = 1; k <= 15; k++)
anc[v][k] = anc[anc[v][k - 1]][k - 1];
Q.push(v);
}
}
}
}
int lca(int u, int v) {
if (dep[u] < dep[v]) swap(u, v);
for (int i = 15; i >= 0; i--)
if (dep[anc[u][i]] >= dep[v]) u = anc[u][i];
if (u == v) return u;
for (int i = 15; i >= 0; i--)
if (anc[u][i] != anc[v][i]) u = anc[u][i], v = anc[v][i];
return anc[u][0];
}
void modify(int x, int &res) {
vis[x] ^= 1;
if (vis[x] == 0) {
cnt[a[x]]--;
if (!cnt[a[x]]) res--;
} else {
if (!cnt[a[x]]) res++;
cnt[a[x]]++;
}
}
int main(void) {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> a[i], rec.push_back(a[i]);
memset(head, -1, sizeof(head));
init_hash();
for (int i = 1, u, v; i <= n - 1; i++) {
cin >> u >> v;
add(u, v), add(v, u);
}
dfs_euler(1, -1);
bfs_lca();
for (int i = 0; i < m; i++) {
int u, v, l;
cin >> u >> v;
if (fir[u] > fir[v]) swap(u, v);
l = lca(u, v);
if (u == l)
q[i] = node(i, fir[u], fir[v], 0);
else
q[i] = node(i, lst[u], fir[v], l);
}
len = sqrt(idx);
sort(q, q + m);
for (int i = 0, L = 1, R = 0, res = 0; i < m; i++) {
int id = q[i].id, l = q[i].l, r = q[i].r, p = q[i].p;
while (R < r) modify(euler[++R], res);
while (R > r) modify(euler[R--], res);
while (L < l) modify(euler[L++], res);
while (L > l) modify(euler[--L], res);
if (p) modify(p, res);
ans[id] = res;
if (p) modify(p, res);
}
for (int i = 0; i < m; i++) cout << ans[i] << '\n';
return 0;
}
标签:anc,int,res,离线,Coel,dep,maxn,莫队 来源: https://www.cnblogs.com/Coel-Flannette/p/16537377.html