P11364 [NOIP2024] 树上查询
首先有一个经典小结论:\(k > 1\) 时,区间 LCA 的深度为 \(\min\limits_{i = l}^{r-1} \text{dep}_{\operatorname{LCA}(i, i+1)}\).
考虑对每个 \(x = \text{dep}_{\operatorname{LCA}(i, i+1)}\) 在 \(\text{dep}\) 序列中找出最大的区间 \([L_i, R_i]\),使得区间内所有位置的 LCA 深度为 \(x\).
查询 \((l, r, k)\) 即是找所有与 \([l, r]\) 的交至少为 \(k\) 的区间 \([L_i, R_i]\) 中,对应的 \(x\) 值最大的元素。
【交集至少为 \(k\)】可以转化为以下两个条件:
- \(L_i \le r - k + 1 \text{ AND } R_i \ge r\);
- \(l + k - 1 \le R_i \le r \text{ AND } R_i - L_i + 1 \ge k\).
满足任意一个都符合【交集至少为 \(k\)】。
前一个可以对 \(r\) 扫描线。具体地,按 \(r\) 从大到小枚举询问,然后将 \(R_i \ge r\) 的所有区间的 \(L\) 放入数据结构,直接查找即可。
第二个对 \(k\) 扫描线。具体地,将区间按照 \(R_i - L_i + 1\) 从大到小排序,然后按 \(k\) 从大到小枚举询问,不断将 \(R_i - L_i + 1 \ge k\) 的区间插入数据结构,查找 \(R_i \in [l+k-1, r]\) 中的最大答案。
注意以上做法仅限 \(k > 1\) 时有效,要单独处理一下所有 \(k=1\) 的询问。
#include <bits/stdc++.h>
using namespace std;
const int N = 5e5 + 5;
int dep[N], n, u, v, t_l[N], t_r[N], a[N], lc[N], f[N][21];
int q, L[N], R[N], K[N], ans[N];
vector<int> g[N];
void dfs(int u, int fa)
{
dep[u] = dep[fa] + 1;
f[u][0] = fa;
for(int i = 1; i <= 20; i++)
f[u][i] = f[f[u][i - 1]][i - 1];
for(auto v : g[u])
if(v != fa) dfs(v, u);
return;
}
int LCA(int x, int y)
{
if(dep[x] < dep[y]) swap(x, y);
for(int i = 20; i >= 0; i--)
if(dep[f[x][i]] >= dep[y]) x = f[x][i];
if(x == y) return x;
for(int i = 20; i >= 0; i--)
if(f[x][i] != f[y][i]) x = f[x][i], y = f[y][i];
return f[x][0];
}
struct node
{
int l, r, mini, maxi;
} tree[N << 2];
void build(int l, int r, int x)
{
tree[x] = {l, r, (int) 2e9, 0};
if(l == r) return;
int mid = (l + r) >> 1;
if(l <= mid) build(l, mid, x << 1);
if(mid < r) build(mid + 1, r, x << 1 | 1);
return;
}
void pushup(int x)
{
tree[x].mini = min(tree[x << 1].mini, tree[x << 1 | 1].mini);
tree[x].maxi = max(tree[x << 1].maxi, tree[x << 1 | 1].maxi);
}
void update(int p, int k, int x)
{
if(p == tree[x].l && p == tree[x].r)
return (void) (tree[x].mini = tree[x].maxi = k);
int mid = (tree[x].l + tree[x].r) >> 1;
if(p <= mid) update(p, k, x << 1);
if(mid < p) update(p, k, x << 1 | 1);
pushup(x);
return;
}
int query(int l, int r, int x)
{
if(l > r) return 0;
if(l <= tree[x].l && tree[x].r <= r) return tree[x].maxi;
int mid = (tree[x].l + tree[x].r) >> 1, res = 0;
if(l <= mid) res = max(res, query(l, r, x << 1));
if(mid < r) res = max(res, query(l, r, x << 1 | 1));
return res;
}
int queryl(int k, int x)
{
if(tree[x].l == tree[x].r) return tree[x].l;
if(tree[x << 1 | 1].mini < k) return queryl(k, x << 1 | 1);
return queryl(k, x << 1);
}
int queryr(int k, int x)
{
if(tree[x].l == tree[x].r) return tree[x].l;
if(tree[x << 1].mini < k) return queryr(k, x << 1);
return queryr(k, x << 1 | 1);
}
struct AO
{
int l, r, v;
} aoao[N];
inline bool cmp_rAO(AO x, AO y)
{
return x.r > y.r;
}
inline bool cmp_lenAO(AO x, AO y)
{
return x.r - x.l + 1 > y.r - y.l + 1;
}
struct Q
{
int l, r, k, id;
} qs[N];
inline bool cmp_r(Q x, Q y)
{
return x.r > y.r;
}
inline bool cmp_k(Q x, Q y)
{
return x.k > y.k;
}
int main()
{
ios :: sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> n;
for(int i = 1; i < n; i++)
{
cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
dfs(1, 0);
for(int i = 1; i < n; i++)
lc[i] = LCA(i, i + 1), a[i] = dep[lc[i]];
// cout <<'\n';
build(1, n - 1, 1);
for(int i = 1; i < n; i++)
{
update(i, a[i], 1);
t_l[i] = queryl(a[i], 1);
if(tree[1].mini >= a[i]) t_l[i] = 1;
else if(t_l[i] > i || a[t_l[i]] >= a[i]) t_l[i] = i;
else t_l[i]++;
}
build(1, n - 1, 1);
for(int i = n - 1; i >= 1; i--)
{
update(i, a[i], 1);
t_r[i] = queryr(a[i], 1);
// t_r[i] = queryr(a[i], 1);
if(tree[1].mini >= a[i]) t_r[i] = n - 1;
else if(t_r[i] < i || a[t_r[i]] >= a[i]) t_r[i] = i;
else t_r[i]--;
t_r[i]++;
aoao[i] = {t_l[i], t_r[i], a[i]};
// c
}
// for(int i = 1; i < n; i++)
// cout << aoao[i].l << ' ' << aoao[i].r << ' ' << aoao[i].v << '\n';
cin >> q;
for(int i = 1; i <= q; i++)
cin >> qs[i].l >> qs[i].r >> qs[i].k, qs[i].id = i;
// reverse(qr.begin(), qr.end());
build(1, n, 1);
sort(aoao + 1, aoao + n, cmp_rAO);
sort(qs + 1, qs + q + 1, cmp_r);
//
for(int i = 1, j = 1; i <= q; i++)
{
while(j < n && aoao[j].r >= qs[i].r)
update(aoao[j].l, aoao[j].v, 1), j++;
ans[qs[i].id] = max(ans[qs[i].id], query(1, qs[i].r - qs[i].k + 1, 1));
}
build(1, n, 1);
sort(aoao + 1, aoao + n, cmp_lenAO);
sort(qs + 1, qs + q + 1, cmp_k);
// for(int i = 1; i <= q; i++)
// cout << qs[i].l << ' ' << qs[i].r << ':' <<qs[i].k << '\n';
for(int i = 1, j = 1; i <= q; i++)
{
if(qs[i].k == 1) continue;
// cout << i << ',' << j << '\n';
while(j < n && aoao[j].r - aoao[j].l + 1 >= qs[i].k)
update(aoao[j].r, aoao[j].v, 1), j++;
ans[qs[i].id] = max(ans[qs[i].id], query(qs[i].l + qs[i].k - 1, qs[i].r, 1));
}
build(1, n, 1);
for(int i = 1; i <= n; i++) update(i, dep[i], 1);
for(int i = 1; i <= q; i++)
{
if(qs[i].k != 1) continue;
ans[qs[i].id] = max(ans[qs[i].id], query(qs[i].l, qs[i].r, 1));
}
for(int i = 1; i <= q; i++) cout << ans[i] << '\n';
// for(int i = 1; i <= n; i++)
// {
// cout << i << ' ' << t_l[i] << ' ' << t_r[i] << '\n';
// }
return 0;
}
望穿寂夜晨曦至,雄鹰展翅图九天。

浙公网安备 33010602011771号