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();
}
posted @ 2026-08-28 01:32  CuteNess  阅读(7)  评论(0)    收藏  举报