题解 P6177 Count on a tree II/【模板】树在线莫队
哦,这是树上链数颜色问题,显然是板,直接树上莫队就……
啊?什么叫强制在线?
哦,好吧,那就只能写……
在线莫队了。
正文
算法介绍
在线莫队,其实说白了就是一个利用了莫队思想的分块,这题也可以直接拿来做。
简述一下,就是预先处理一些莫队的状态,然后在线询问就从这些状态上转移。
更详细点的也许可以看看这个?
不考虑在线,树上莫队做法显然,拍成欧拉序然后莫队就行。
然后考虑改成在线莫队。
根据上述文章,发现树上莫队需要维护的两个数组:\(cnt\) 和 \(vis\),也就是颜色出现次数和访问某节点的次数的奇偶性。是不太可以用简单的前缀和相减维护的(一个主要原因是树上莫队的加或减与其在序列上的位置没有固定关系,因而不满足可差分性),所以只能考虑维护每两块间的莫队数组,然后查询的话就模拟莫队指针移动过程就行。
正确性证明
块长开到 \(\frac n {\sqrt[3]{m}}\) 最优,时空复杂度都来到 \(O(nm^\frac 23)\)。
关于原因,容易发现预处理时间是每两块处理一次,如上块长复杂度来到 \(O(nm^\frac 23)\),然后每次查询,指针最多移动块长次,一共 \(m\) 次查询,所以复杂度也是 \(O(nm^\frac 23)\)。
来自一天后:发现预处理莫队数组满足下一个由上一个更新而来,也就是说,这种比较难维护的莫队数组,也许可以考虑用可持久化数组(主席树)来做,空间时间都是 \(O(n\sqrt m \log n)\),看起来好像更优了。
代码实现
一般在线莫队的常数很大,给一个非常有效的卡常方案:
查询的时候,我们找到离 \(l,r\) 最近的块边界,然后移动这个边界,比人为规定选择左或右边界要快得多。
// code by 樓影沫瞬_Hz17
#include <bits/stdc++.h>
constexpr int N = 4e4 + 10, M = 1e5 + 10;
struct edge {
int to, nxt;
} e[N * 2]; int hd[N];
inline void add(int u, int v) {
static int tot = 1;
e[++ tot] = {v, hd[u]}, hd[u] = tot;
e[++ tot] = {u, hd[v]}, hd[v] = tot;
}
int cne, rr[N * 2], st[N], ed[N];
int dep[N], fa[N], sz[N], wc[N], top[N];
inline void dsf(int u, int f) {
dep[u] = dep[f] + 1, fa[u] = f, sz[u] = 1;
for(int i = hd[u], v = e[i].to; i; i = e[i].nxt, v = e[i].to) if(v != f) {
dsf(v, u);
sz[u] += sz[v];
if(sz[wc[u]] < sz[v]) wc[u] = v;
}
}
inline void dfs(int u, int tp) {
top[u] = tp, st[u] = ++ cne, rr[cne] = u;
if(wc[u]) dfs(wc[u], tp);
for(int i = hd[u], v = e[i].to; i; i = e[i].nxt, v = e[i].to) if(v != fa[u] and v != wc[u])
dfs(v, v);
ed[u] = ++cne, rr[cne] = u;
}
inline int lca(int a, int b) {
while(top[a] != top[b]) dep[top[a]] > dep[top[b]] ? a = fa[top[a]] : b = fa[top[b]];
return dep[a] > dep[b] ? b : a;
}
constexpr int B = 1100, T = N * 2 / B + 10;
int x[N], a[N], n, m, V;
struct Moduis {
uint16_t cnt[N];
int ans;
bool vis[N];
void change(int p) { (vis[p] ^= 1) ? ans += !cnt[a[p]] ++ : ans -= ! --cnt[a[p]]; }
} mas[T][T];
bool vis[N]; int ans;
uint16_t cnt[N], pos[N * 2];
int L[T], R[T];
inline void change(int p) { (vis[p] ^= 1) ? ans += !cnt[a[p]] ++ : ans -= !-- cnt[a[p]]; }
signed main() {
#ifndef ONLINE_JUDGE
freopen("in.ru", "r", stdin);
freopen("out.ru", "w", stdout);
#endif
using std::cin; using std::cout;
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin >> n >> m;
for(int i = 1; i <= n; i ++) cin >> a[i], x[i] = a[i];
for(int i = 1, u, v; i < n; i ++) {
cin >> u >> v;
add(u, v);
}
std::sort(&x[1], &x[n] + 1); V = std::unique(&x[1], &x[n] + 1) - x - 1;
for(int i = 1; i <= n; i ++) a[i] = std::lower_bound(&x[1], &x[n] + 1, a[i]) - x;
dsf(1, 1), dfs(1, 1);
for(int i = 1; R[i - 1] != cne; i ++) {
L[i] = (i - 1) * B + 1, R[i] = std::min(B * i, cne);
for(int j = L[i]; j <= R[i]; j ++) pos[j] = i;
}
for(int i = 1; R[i - 1] != cne; i ++) {
for(int j = i + 1; R[j - 1] != cne; j ++) {
for(int k = L[i + 1]; k <= R[j - 1]; k ++) mas[i][j].change(rr[k]);
}
}
for(int c = 1, u, v, l, r, lp, rp, ql, qr; c <= m; c ++) {
cin >> u >> v; u ^= ans;
if(st[u] > st[v]) std::swap(u, v);
int lf = lca(u, v);
if(lf == u) l = st[u], r = st[v], lf = 0;
else l = ed[u], r = st[v];
ql = pos[l], qr = pos[r];
if(ql == qr) {
ans = 0;
cnt[a[lf]] = vis[lf] = 0;
for(int i = l; i <= r; i ++) cnt[a[rr[i]]] = vis[rr[i]] = 0;
for(int i = l; i <= r; i ++) change(rr[i]);
if(lf) change(lf);
}
else {
if(l - R[ql - 1] < R[ql] - l and ql != 1) ql --;
if(L[qr + 1] - r < r - L[qr] and qr != pos[cne]) qr ++;
lp = R[ql], rp = L[qr];
ans = mas[ql][qr].ans;
cnt[a[lf]] = mas[ql][qr].cnt[a[lf]];
vis[lf] = mas[ql][qr].vis[lf];
for(int i = l; i <= lp; i ++) cnt[a[rr[i]]] = mas[ql][qr].cnt[a[rr[i]]],
vis[rr[i]] = mas[ql][qr].vis[rr[i]];
for(int i = lp + 1; i < l; i ++) cnt[a[rr[i]]] = mas[ql][qr].cnt[a[rr[i]]],
vis[rr[i]] = mas[ql][qr].vis[rr[i]];
for(int i = rp; i <= r; i ++) cnt[a[rr[i]]] = mas[ql][qr].cnt[a[rr[i]]],
vis[rr[i]] = mas[ql][qr].vis[rr[i]];
for(int i = r + 1; i < rp; i ++) cnt[a[rr[i]]] = mas[ql][qr].cnt[a[rr[i]]],
vis[rr[i]] = mas[ql][qr].vis[rr[i]];
for(int i = l; i <= lp; i ++) change(rr[i]);
for(int i = lp + 1; i < l; i ++) change(rr[i]);
for(int i = rp; i <= r; i ++) change(rr[i]);
for(int i = r + 1; i < rp; i ++) change(rr[i]);
if(lf) change(lf);
}
cout << ans << '\n';
}
}
// 星間~ 干渉~ 融解~ 輪迴~ 邂逅~ 再生~ ララバイ~

浙公网安备 33010602011771号