牛客练习赛42E 热爆了(Lct+树状数组)
牛客练习赛42E 热爆了(Lct+树状数组)
解题思路
小 X 决定出一道送温暖题来和大家一起愉悦
他给了你一棵 n 个节点的树,每个点有个点权
ai
现在他给了你 Q 个询问,每次会给定 L,R ,然后定义满足
ai∈[L,R] 的点 i 为关键点
你需要回答出满足下列至少一个条件的点 x 的个数:1. x 是关键点2. 在树上删去 x 和所有与其相连的边后,存在两个关键点 a,b ,使得 a 和 b 不连通
数据范围
\[1 \le n \le 10^5
\]
解题思路
神仙 ztb 学长给出了一个神仙的线段树合并做法,我想了想有一共点分树 + lct + 树状数组的方法,又想了一下,点分树完全可以扔了,下面给出一个 \(\Theta(n\log n)\) 的 LCT + 树状数组做法吧
这道题讲的就是求一个包含所有关键点的联通块大小
有两个套路如果知道那么这题将会很好想到
第一个套路:找到 \([L,R]\) 所有点的 \(Lca\),易知 \(Lca\) 肯定在联通块内,且除了 \(Lca\) 子树的节点不会在联通块内,也就是我们直接统计 \([L,R]\) 到 \(Lca\) 的路径之并的大小即可,一开始我想建个点分树,结果发现直接统计 \([L,R]\) 到根的路径再将 \(Lca\) 到根的路径减去即可,求 \(Lca\) 可以用三个 st 表实现
第二个套路:将所有询问离线下来,按 R 排序,我们把所有贡献尽可能的放到靠右的点上,具体来说,用个树状数组维护,加入 R 时,把 R 到根节点的路径长度加到 R 位置上,但这条路径可能会和以前的路径有重合,要把以前的贡献删掉,容易发现用 LCT 可以很好的维护这个问题
所以我们较为简单的实现了这题
#pragma GCC optimize(3, "inline")
#include <queue>
#include <vector>
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define MP make_pair
#define ll long long
#define fi first
#define se second
using namespace std;
template <typename T>
void read(T &x) {
x = 0; bool f = 0;
char c = getchar();
for (;!isdigit(c);c=getchar()) if (c=='-') f=1;
for (;isdigit(c);c=getchar()) x=x*10+(c^48);
if (f) x=-x;
}
template<typename F>
inline void write(F x, char ed = '\n')
{
static short st[30];short tp=0;
if(x<0) putchar('-'),x=-x;
do st[++tp]=x%10,x/=10; while(x);
while(tp) putchar('0'|st[tp--]);
putchar(ed);
}
template <typename T>
inline void Mx(T &x, T y) { x < y && (x = y); }
template <typename T>
inline void Mn(T &x, T y) { x > y && (x = y); }
const int N = 200500;
int cnt[N], a[N], c[N];
int lg[N], st[N][18], d[N], n;
inline void Add(int x, int c) {
for (; x <= n; x += x & -x) d[x] += c;
}
inline int query(int x) {
int res = 0;
for (; x; x -= x & -x) res += d[x];
return res;
}
namespace LCT {
#define ls son[x][0]
#define rs son[x][1]
int son[N][2], f[N], siz[N], col[N];
bool nroot(int x) {
return son[f[x]][0] == x || son[f[x]][1] == x;
}
void update(int x) {
siz[x] = siz[ls] + siz[rs] + 1;
}
void rotate(int x) {
int y = f[x], z = f[y];
int k = son[y][1] == x, w = son[x][!k];
if (nroot(y)) son[z][son[z][1]==y] = x; f[x] = z;
f[y] = x, son[x][!k] = y;
if (w) f[w] = y; son[y][k] = w;
update(y);
}
void spread(int x) {
if (ls) col[ls] = col[x];
if (rs) col[rs] = col[x];
}
int st[N];
void splay(int x) {
int y = x, z = 0; st[++z] = y;
while (nroot(y)) st[++z] = y = f[y];
while (z) spread(st[z--]);
while (nroot(x)) {
int y = f[x], z = f[y];
if (nroot(y)) {
if ((son[z][0] == y) ^ (son[y][0] == x)) rotate(x);
else rotate(y);
}
rotate(x);
}
update(x);
}
void access(int x, int i) {
for (int y = 0; x; x = f[y = x]) {
splay(x); siz[x] -= siz[rs], (rs = y) && (f[y] = x);
if (col[x]) Add(col[x], -siz[x]);
Add(i, siz[x]), update(x); col[x] = i;
}
}
}
struct St1 {
int st[N<<1][19], n;
void init(void) {
for (int j = 1;j <= 20; j++)
for (int i = 1;i + (1 << j) - 1 <= n; i++)
st[i][j] = min(st[i][j-1], st[i+(1<<(j-1))][j-1]);
}
inline int query(int l, int r) {
int t = lg[r - l + 1];
return min(st[l][t], st[r-(1<<t)+1][t]);
}
}s1, s2;
struct St2 {
int st[N][19], n;
void init(void) {
for (int j = 1;j <= 20; j++)
for (int i = 1;i + (1 << j) - 1 <= n; i++)
st[i][j] = max(st[i][j-1], st[i+(1<<(j-1))][j-1]);
}
inline int query(int l, int r) {
int t = lg[r - l + 1];
return max(st[l][t], st[r-(1<<t)+1][t]);
}
}s3;
int dep[N], ne[N<<1], to[N<<1], h[N], tot, cc;
inline void add(int x, int y) {
ne[++tot] = h[x], to[h[x] = tot] = y;
}
void dfs(int x, int fa) {
LCT::f[x] = fa, LCT::siz[x] = 1;
s2.st[++cc][0] = dep[x], s1.st[a[x]][0] = s3.st[a[x]][0] = cc;
for (int i = h[x]; i; i = ne[i]) {
int y = to[i]; if (y == fa) continue;
dep[y] = dep[x] + 1, dfs(y, x);
s2.st[++cc][0] = dep[x];
}
}
int Dep(int l, int r) {
int x = s1.query(l, r), y = s3.query(l, r);
return s2.query(x, y);
}
vector<pair<int, int> > v[N];
int pos[N], ans[N<<2], q;
int main() {
// freopen ("hs.in","r",stdin);
// freopen ("hs.out","w",stdout);
read(n), read(q);
for (int i = 1;i <= n; i++) read(a[i]), c[i] = a[i];
sort(c + 1, c + n + 1);
for (int i = 1;i <= n; i++) {
a[i] = lower_bound(c + 1, c + n + 1, a[i]) - c;
cnt[a[i]]++, a[i] += cnt[a[i]] - 1, pos[a[i]] = i;
}
for (int i = 1, x, y;i < n; i++)
read(x), read(y), add(x, y), add(y, x);
dfs(1, 0), s2.n = cc, s1.n = s3.n = n;
for (int i = 2;i <= cc; i++) lg[i] = lg[i>>1] + 1;
s1.init(), s2.init(), s3.init();
for (int i = 1, l, r;i <= q; i++) {
read(l), read(r);
l = lower_bound(c + 1, c + n + 1, l) - c;
r = upper_bound(c + 1, c + n + 1, r) - c - 1;
if (l > r) continue; ans[i] -= Dep(l, r);
v[r].emplace_back(l, i);
}
for (int i = 1;i <= n; i++) {
LCT::access(pos[i], i);
for (auto t: v[i])
ans[t.se] += query(i) - query(t.fi - 1);
}
for (int i = 1;i <= q; i++) write(ans[i]);
return 0;
}