P5311 成都七中
给定树,点带颜色和编号,编号构成排列。\(q\) 次询问:
- 仅保留树上编号 \(\in [l,r]\) 的点及其间的边,询问点 \(x\) 所在连通块颜色数。
\[n,q\le 10^5
\]
考虑把连通块用路径刻画,对于连通块中的点 \(y\),限制即为 \(x\rightarrow y\) 路径上点编号最小值 \(\ge l\),最大值 \(\le r\)。
那么路径问题考虑点分治,设分治区域根为 \(p\),对 \(u\) 处理出 \(p\rightarrow u\) 路径上编号最小/最大值 \(L_u,R_u\)。
颜色数考虑把相互包含的区间保留小的,那么剩下的区间 \(L,R\) 不降。此时对 \(r\) 扫描线,每次删去上一个 \(L\) 再在当前 \(L\) 加 \(1\)。
此即维护当前颜色最大的 \(L\),查询时对 \(l\) 做后缀和即可。
还有一个问题是可能在点分树上不同的祖先处会算重,我们考虑第一个合法的 \(p\) 处的值即为答案。
考虑若 \(p\rightarrow x\) 合法,则对任意合法 \(x\rightarrow y\),\(x\rightarrow p\ +\ p\rightarrow y\) 仍然合法,因此点分树上各个祖先上统计到的范围互相包含,那么最浅处范围最大。
邀请所有人品鉴单函数淀粉质!
#include <algorithm>
#include <iostream>
#include <vector>
#include <tuple>
const int N = 1e5 + 7;
#define rep(i,a,b) for(int i(a);i<=(b);++i)
int n, m, c[N];
std::basic_string<int> g[N];
int ans[N];
std::vector<std::tuple<int, int, int>> qry[N];
int vis[N], xfa[N];
auto Ct = [](auto&& f, auto&&...args) { f(f, args...); };
struct BIT {
int f[N];
inline void add(int u, int y) {
u = N - u - 1;
for(; u < N; u += u & -u)
f[u] += y;
}
inline int get(int u) {
u = N - u - 1; int y = 0;
for(; u; u -= u & -u)
y += f[u];
return y;
}
inline void clear(int u) {
u = N - u - 1;
for(; u < N; u += u & -u)
f[u] = 0;
}
} T;
inline void work(int t) {
static int siz[N];
Ct([&](auto&& ptr, int u, int fa) -> void {
siz[u] = 1;
for(int& v: g[u]) {
if(v == fa || vis[v]) continue;
ptr(ptr, v, u), siz[u] += siz[v];
}
}, t, 0); // calcsiz
int rt = t, nrt = siz[t];
Ct([&](auto&& ptr, int u, int fa) -> void {
int spt = 0;
for(int& v: g[u]) {
if(v == fa || vis[v]) continue;
ptr(ptr, v, u), spt = std::max(spt, siz[v]);
}
spt = std::max(spt, siz[t] - siz[u]);
if(spt < nrt) nrt = spt, rt = u;
}, t, 0); // findroot
struct rtx1 {int l, r, id; };
std::vector<rtx1> qri;
struct rtx2 {int x, l, r, p; };
std::vector<rtx2> modf;
Ct([&](auto&& ptr, int u, int fa, int U, int D) -> void {
U = std::max(U, u), D = std::min(D, u);
modf.push_back({u, D, U, 0});
for(auto& [l, r, id]: qry[u]) {
if(l <= D && U <= r && !ans[id]) {
qri.push_back({l, r, id});
}
}
for(int& v: g[u]) {
if(vis[v] || v == fa) continue;
ptr(ptr, v, u, U, D);
}
}, rt, 0, 0, N); // solve
std::sort(modf.begin(), modf.end(), [](auto&& x, auto&& y) {
int a = c[x.x], b = c[y.x];
return a == b ? x.r < y.r : a < b;
});
int mc = 0, ml = 0;
for(auto& [u, l, r, p]: modf) {
if(c[u] != mc) mc = c[u], ml = l, p = 0;
else if(l > ml) p = ml, ml = l;
else p = -1;
}
std::sort(modf.begin(), modf.end(), [](auto&& x, auto&& y) { return x.r < y.r; });
std::sort(qri.begin(), qri.end(), [](auto&& x, auto&& y) { return x.r < y.r; });
std::basic_string<int> toclear;
int pt = 0;
for(auto& [l, r, id]: qri) {
while(pt < (int)modf.size() && modf[pt].r <= r) {
if(modf[pt].p >= 0) {
T.add(modf[pt].l, 1);
toclear += modf[pt].l;
if(modf[pt].p > 0) {
T.add(modf[pt].p, -1);
}
}
++pt;
}
ans[id] = T.get(l);
}
for(auto& p: toclear) T.clear(p);
vis[rt] = 1;
for(int& v: g[rt])
if(!vis[v]) work(v);
}
inline void solve() {
std::cin >> n >> m;
rep(i, 1, n) std::cin >> c[i];
for(int x, y, t = n; --t; )
std::cin >> x >> y, g[x] += y, g[y] += x;
rep(i, 1, m) {
int x, l, r; std::cin >> l >> r >> x;
qry[x].emplace_back(l, r, i);
}
work(1);
rep(i, 1, m) {
std::cout << ans[i] << "\n";
}
}
int main() {
std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
solve();
}
本文来自博客园,作者:CuteNess,转载请注明原文链接:https://www.cnblogs.com/CuteNess/p/22730314

浙公网安备 33010602011771号