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;
}
posted @ 2025-06-05 21:45  心灵震荡  阅读(40)  评论(0)    收藏  举报