Loading

NOIP2024 树上查询

link

考虑树上启发式合并维护所有连续段,不难发现我们可以造出 \(\mathcal O(n\log n)\) 个连续段。

如果观察得比较仔细,对于每个 \(i\)\((i, i + 1)\) 只会在一个点合并一次,每次合并至多产生一个连续段,所以本质不同的连续段个数为 \(\mathcal O(n)\),可以使用并查集。

考虑我们现在有 \(\mathcal O(n)\) 个区间以及权值 \((l_i, r_i, w_i)\),有若干个询问区间 \([l_q, r_q]\),求 \(\max\limits_{[l_i, r_i] \cap [l_q, r_q] \cap \mathbf N \ge k_q} w_i\)

分成四类:

  • \(l_i \le l_q \le r_i \le r_q\)

  • \(l_q \le l_i \le r_q \le r_i\)

  • \(l_i \le l_q \le r_q \le r_i\)

  • \(l_q \le l_i \le r_i \le r_q\)

第三类直接二维数点,是容易的。

第一类和第四类可以考虑这样做:按 \(r_i - l_i + 1\)\(k_q\) 排序,依次插入。插入 \((l_i, r_i, w_i)\) 时在位置 \(l_i\) chkmax \(w_i\),查询 \([l_q, r_q]\) 时求位置 \(l_q \sim r_q - k_q + 1\) 上的最大值。

第二类同理,不算并查集的话时间复杂度为 \(\mathcal O(n\log n)\)

  • 启示:启发式合并维护连续段;不同形式的二维数点。
点击查看代码
//#pragma GCC optimize(1)
//#pragma GCC optimize(2)
//#pragma GCC optimize(3, "Ofast", "inline")
#include <bits/stdc++.h>
namespace Initial {
	#define ll long long
	#define ull unsigned long long
	#define fi first
	#define se second
	#define mkp make_pair
	#define pir pair <ll, ll>
	#define pb emplace_back
	#define i128 __int128
	using namespace std;
	template <class T>
	void rd(T &x) {
		char ch; bool f = 0;
		while(!isdigit(ch = getchar()))
			if(ch == '-') f = 1;
		x = ch - '0';
		while(isdigit(ch = getchar()))
			x = (x << 1) + (x << 3) + ch - '0';
		if(f) x = -x;
	}
	void write(const ll x, const char str[] = "") {
		if(x > 9) write(x / 10);
		putchar(x % 10 + '0'), printf("%s", str);
	}
	const ll maxn = 5e5 + 10, inf = 1e18, mod = 1e9 + 7;
	ll power(ll a, ll b = mod - 2) {
		ll s = 1;
		while(b) {
			if(b & 1) s = 1ll * s * a %mod;
			a = 1ll * a * a %mod, b >>= 1;
		} return s;
	}
	template <class T>
	const ll pls(const T x, const T y) { return x + y >= mod? x + y - mod : x + y; }
	template <class T>
	void add(T &x, const T y) { x = x + y >= mod? x + y - mod : x + y; }
	template <class T>
	void chkmax(T &x, const T y) { x = x < y? y : x; }
	template <class T>
	void chkmin(T &x, const T y) { x = x > y? y : x; }
} using namespace Initial;

struct DSU {
	ll d[maxn];
	ll find(ll x) { return d[x] ^ x? d[x] = find(d[x]) : x; }
	void merge(ll x, ll y) { d[find(x)] = find(y); }
} Left, Right;

ll n, siz[maxn], son[maxn], q, ti, dfn[maxn], out[maxn], idfn[maxn];
vector <ll> to[maxn]; ll dep[maxn], mged[maxn];

void dfs1(ll u, ll fa = 0) {
	siz[u] = 1, idfn[dfn[u] = ++ti] = u, dep[u] = dep[fa] + 1;
	for(ll v: to[u])
		if(v ^ fa) {
			dfs1(v, u), siz[u] += siz[v];
			if(siz[v] > siz[son[u]]) son[u] = v;
		} out[u] = ti;
}

struct seg { ll l, r, w; } a[maxn << 1]; ll m;
struct qur { ll l, r, k, id; } b[maxn];

vector <ll> ffc;

void add(ll x) {
	Left.merge(x, x - 1), Right.merge(x, x + 1);
	ll flg = 0;
	if(Left.find(x) < x - 1 && !mged[x - 1]) flg = 1, mged[x - 1] = 1;
	if(Right.find(x) > x + 1 && !mged[x]) flg = 1, mged[x] = 1;
	if(flg) ffc.pb(x);
}

void dfs2(ll u, ll fa = 0) {
	for(ll v: to[u])
		if(v != fa && v != son[u]) {
			dfs2(v, u);
			for(ll i = dfn[v], x; i <= out[v]; i++)
				x = idfn[i], Left.d[x] = Right.d[x] = x;
		}
	if(son[u]) dfs2(son[u], u); ffc.clear();
	for(ll v: to[u])
		if(v != fa && v != son[u])
			for(ll i = dfn[v]; i <= out[v]; i++)
				add(idfn[i]);
	add(u);
	for(ll x: ffc) a[++m] = (seg) { Left.find(x) + 1,
	 Right.find(x) - 1, dep[u] };
}

struct SGT {
	ll mx[maxn << 2];
	void modify(ll p, ll l, ll r, ll x, ll v) {
		chkmax(mx[p], v); if(l == r) return;
		ll mid = l + r >> 1;
		if(x <= mid) modify(p << 1, l, mid, x, v);
		else modify(p << 1|1, mid + 1, r, x, v);
	}
	ll query(ll p, ll l, ll r, ll ql, ll qr) {
		if(ql <= l && r <= qr) return mx[p];
		if(r < ql || qr < l) return -inf;
		ll mid = l + r >> 1;
		return max(query(p << 1, l, mid, ql, qr),
		 query(p << 1|1, mid + 1, r, ql, qr));
	}
} tr; ll ans[maxn], tree[maxn];

void tr_add(ll x, ll v) {
	for(; x <= n; x += x & -x) chkmax(tree[x], v);
}

ll tr_ask(ll x) {
	ll v = 0;
	for(; x; x -= x & -x) chkmax(v, tree[x]);
	return v;
}

int main() {
//	freopen("query.in", "r", stdin);
//	freopen("query.out", "w", stdout);
	rd(n);
	for(ll i = 0; i <= n + 1; i++) Left.d[i] = Right.d[i] = i;
	for(ll i = 1; i < n; i++) {
		ll u, v; rd(u), rd(v);
		to[u].pb(v), to[v].pb(u);
	} dfs1(1);
	dfs2(1);
	sort(a + 1, a + 1 + m, [](seg a, seg b) {
		return a.r - a.l > b.r - b.l;
	}); rd(q);
	for(ll i = 1; i <= q; i++)
		rd(b[i].l), rd(b[i].r), rd(b[b[i].id = i].k);
	sort(b + 1, b + 1 + q, [](qur a, qur b) {
		return a.k > b.k;
	});
	for(ll i = 1; i <= n; i++) a[++m] = (seg) { i, i, dep[i] };
	for(ll i = 1, j = 1; i <= q; i++) {
		while(j <= m && a[j].r - a[j].l + 1 >= b[i].k)
			tr.modify(1, 1, n, a[j].l, a[j].w), ++j;
		chkmax(ans[b[i].id], tr.query(1, 1, n, b[i].l, b[i].r - b[i].k + 1));
	} tr = {};
	for(ll i = 1, j = 1; i <= q; i++) {
		while(j <= m && a[j].r - a[j].l + 1 >= b[i].k)
			tr.modify(1, 1, n, a[j].r, a[j].w), ++j;
		chkmax(ans[b[i].id], tr.query(1, 1, n, b[i].l + b[i].k - 1, b[i].r));
	}
	sort(a + 1, a + 1 + m, [](seg a, seg b) {
		return a.r > b.r;
	});
	sort(b + 1, b + 1 + q, [](qur a, qur b) {
		return a.r > b.r;
	});
	for(ll i = 1, j = 1; i <= q; i++) {
		while(j <= m && a[j].r >= b[i].r) tr_add(a[j].l, a[j].w), ++j;
		chkmax(ans[b[i].id], tr_ask(b[i].l));
	}
	for(ll i = 1; i <= q; i++) write(ans[i], "\n");
	return 0;
}
posted @ 2024-12-01 20:44  Sktn0089  阅读(253)  评论(0)    收藏  举报