「NOI2026」传送 + 木棉

传送

首先我们可以挖掘一个性质就是对于相同的 \(y\),每个点一定只有两种选择,要么往 \(y\) 的方向走,要么随机跳,进一步发现就是走随机的点一定是从距离 \(y\) 远的距离开始的。

然后我们推一下式子就可以求出来随机点的答案,我们设深度为 \(i\) 的点的数量为 \(a_i\),那么答案就是:

\[R_k = \frac{n + \sum_{i=1}^k{i \times a_i}}{\sum_{i=1}^{k} a_i} \]

\(k\) 是你枚举的深度。

这个 \(R\) 函数直接点分树可以很轻松的做到 \(\log\) 询问单点值。

然后我们进一步发现这个 \(R\) 函数是单谷的,于是我们就可以对于每个 \(i\),二分求出来最低值,于是就可以做到 \(O(n \log^2 n + q)\)

然后我们发现相邻的两个点,最优值的位置一定相差不会大于 \(1\),因为如果相差大于 \(1\),我们直接从小的走过去就可以更优,然后就只需要在最开始的点进行一次二分就行了,剩下的只需要求出来 \(O(1)\) 个值。

总的复杂度就变成了 \(O(n \log n + q)\),只要不写假就可以轻松通过。

点击查看代码
#include <bits/stdc++.h>
#include "teleport.h"
using namespace std;
#define ll long long

int n, m;
const int N = 5e5 + 10, M = 1e6 + 10;
vector<int> e[N]; 
int siz[N];
int fa[N];
int dep[N];
int st[N * 2][21];
int pre[N * 2];
int mask = 0;
int fir[N];
int idx;
void dfs(int now, int ff){
	fa[now] = ff;
	dep[now] = dep[ff] + 1;
	siz[now] = 1;
	st[++idx][0] = dep[now];
	fir[now] = idx;
	mask = max(mask, dep[now] - 1);
	for(auto y: e[now]){
		if(y == ff) continue;
		dfs(y, now); 
		st[++idx][0] = dep[now];
	}
}
int LCA(int x, int y){
	if(fir[x] > fir[y]) swap(x, y);
	x = fir[x]; y = fir[y];
	int step = pre[y - x + 1];
	return min(st[x][step], st[y - (1ll << step) + 1][step]);
}
int dis(int x, int y){
	// cerr << x << ' ' << y << '\n';
	return dep[x] + dep[y] - LCA(x, y) * 2;
}
int tval[M];
bool ok[N];
int nfa[N];
vector<int> val1[N], val3[N];
vector<ll> val2[N], val4[N];
void dfs_col(int now, int ff, int rt, int tr, int ds){
	// if(ff)
	// dist[dp][now] = dist[dp][ff] + 1;
	if(val1[rt].size() <= ds){
		val1[rt].resize(ds + 1);
		val2[rt].resize(ds + 1);
	}
	val1[rt][ds] += 1;
	val2[rt][ds] += ds;
	// ls[rt].push_back(now);
	if(val3[tr].size() <= ds){
		val3[tr].resize(ds + 1);
		val4[tr].resize(ds + 1);
	}
	val3[tr][ds] += 1;	
	val4[tr][ds] += ds;
	for(auto y: e[now]){
		if(ok[y] || y == ff) continue;
		dfs_col(y, now, rt, tr, ds + 1);
	}
}
int rt;
int mxl[N];
void get_rt(int now, int ff, int tot){
	mxl[now] = 0;
	siz[now] = 1;
	for(auto y: e[now]){
		if(ok[y] || y == ff) continue;
		get_rt(y, now, tot);
		mxl[now] = max(mxl[now], siz[y]);
		siz[now] += siz[y]; 
	}
	mxl[now] = max(mxl[now], tot - siz[now]);
	if(mxl[now] < mxl[rt] || !rt){
		rt = now;
	}
}
int ndep[N];
void divide(int now, int ff){
	ok[now] = 1;
	nfa[now] = ff;
	ndep[now] = ndep[ff] + 1;
	val1[now].resize(1);
	val2[now].resize(1);
	val1[now][0] = 1;
	for(auto y: e[now]){
		if(ok[y]) continue;
		rt = 0;
		get_rt(y, 0, siz[y]);
		dfs_col(y, 0, now, rt, 1);
		divide(rt, now);
	}
}

void build(){
	for(int i = 1; i <= n; i++){
		for(int j = 1; j < val1[i].size(); j++){
			val1[i][j] += val1[i][j - 1];
			val2[i][j] += val2[i][j - 1];
		}
		for(int j = 1; j < val3[i].size(); j++){
			val3[i][j] += val3[i][j - 1];
			val4[i][j] += val4[i][j - 1];
		}
	}
}
ll gcd(ll x, ll y){
	if(x > y) swap(x, y);
	if(!x) return y;
	return gcd(y % x, x);
}
struct Frac{
	ll x, y;
	friend bool operator < (Frac x, Frac y){
		ll tx = x.x * y.y, ty = x.y * y.x;
		return tx < ty;
	}
};
int TTTT = 0;
int pos[N];
Frac get_val(int p, int tdis){
	int np = p;
	ll ans1 = 0, ans2 = 0;
	int val = 0;
	
	while(np){
		if(val <= tdis){
			ans1 += val1[np][min(tdis - val, (int)val1[np].size() - 1)];
			ans2 += val2[np][min(tdis - val, (int)val2[np].size() - 1)];
			ans2 += val1[np][min(tdis - val, (int)val1[np].size() - 1)]  * 1ll * val;
		}
		if(ndep[np] != 1){
			val = dis(nfa[np], p);
			if(val <= tdis){
				ans1 -= val3[np][min(tdis - val, (int)val3[np].size() - 1)];
				ans2 -= val4[np][min(tdis - val, (int)val4[np].size() - 1)];
				ans2 -= val3[np][min(tdis - val, (int)val3[np].size() - 1)] * 1ll * val;
			}	
		}
		np = nfa[np];
	}
	return {n + ans2, ans1};
}
Frac res[N];

void solve(int now, int ff){
	// cerr << now << ' ' << ff << '\n';
	Frac tmp = {n + 10, 1};
	if(!ff){
		// Frac tmp = {n + 10, 1};
		int tpos = 0;
		int l = 0, r = n;
		int ans = 0;
		while(l <= r){
			int mid = (l + r) >> 1;
			if(get_val(now, mid + 1) < get_val(now, mid)){
				l = mid + 1;
				ans = mid;
			}
			else{
				r = mid - 1;
			}
		}
		for(int j = max(ans - 1, 0); j <= ans + 1; j++){
			Frac ntmp = get_val(now, j);
			if(ntmp < tmp){
				tpos = j;
				tmp = ntmp;
			}
		}
		pos[now] = tpos;
	}
	else{
		
		int tpos = 0;
		for(int j = max(0, pos[ff] - 1); j <= pos[ff] + 1; j++){
			Frac ntmp = get_val(now, j);
			if(ntmp < tmp){
				tpos = j;
				tmp = ntmp;
			}
		}
		pos[now] = tpos;
	}
	res[now] = tmp;
	ll ok = gcd(res[now].x, res[now].y);
	res[now].x /= ok;
	res[now].y /= ok;
	for(auto y: e[now]){
		if(y == ff) continue;
		solve(y, now);
	}
}
vector<pair<ll, int>> teleport(int c, int NN, int MM,
		vector<int> u, vector<int> v, vector<int> x, vector<int> y){
	for(int i = 2; i < N * 2; i++){
		pre[i] = pre[i / 2] + 1;
	}
	n = NN;
	m = MM;
	for(int i = 1; i < n; i++){
		e[u[i - 1] + 1].push_back(v[i - 1] + 1);
		e[v[i - 1] + 1].push_back(u[i - 1] + 1);
	}
	dfs(1, 0);

	for(int j = 1; j <= 20; j++){
		for(int i = 1; i + (1ll << j) - 1 <= idx; i++){
			st[i][j] = min(st[i][j - 1], st[i + (1ll << (j - 1))][j - 1]);
		}
	}
	get_rt(1, 0, n);
	divide(rt, 0);
	build();
	solve(1, 0);
	vector<pair<ll, int> > ans(m);
	for(int i = 1; i <= m; i++){
		int td = dis(x[i - 1] + 1, y[i - 1] + 1);
		ans[i - 1].first = (td > pos[y[i - 1] + 1] ? res[y[i - 1] + 1].x : td);
		ans[i - 1].second = (td > pos[y[i - 1] + 1] ? res[y[i - 1] + 1].y : 1);
	}
	return ans;
}

木棉

首先我们思考如何一次询问 \(O(n)\) 求出答案,发现只需要求出来 \(x, y\) 的父亲是谁就行了,然后我们发现对于区间最后内最后一次出现位置小的,才有可能连到另一个,然后维护一个 \(cnt\) 表示现在的叶子有多少个比 \(x\) 小的,然后我们扫描这个区间,对于这个区间内最后一个出现的数,如果比 \(x\) 小,就让 \(cnt\) 加一,然后每次都会使得 \(cnt\) 减一,然后找到第一个位置大于 \(x\) 的最后一个出现位置且 \(cnt <= 0\) 就是 \(x\) 的父亲。

然后我们发现 \(cnt\) 一定单调不增,然后就可以二分,接下来就只需要快速求出来某一个位置的 \(cnt\) 位置就行了,然后这个东西按照询问右端点排序之后可以写成一个在线二维数点的事情,这个可以直接树套树,然后复杂度就是 \(O(n \log^3 n)\)

然后我们发现树套树有一维是下标,我们在下标那一维线段树二分,就可以做到 \(O(n \log^2 n)\),就可以通过,有一些细节就是内层可以直接写树状数组,因为你修改的位置一定是这个区间内的某个值,这样就离散化,空间是 \(O(n \log n)\) 的,常数会小一点。

点击查看代码
#include <bits/stdc++.h>
#include "kapok.h"
using namespace std;
int n, m;
const int N = 2e5 + 10;
int a[N];
struct Bit{
	vector<int> val, tr;
	int siz;
	#define lowbit(x) (x & (-x))
	void build(vector<int> a){
		val = a;
		sort(val.begin(), val.end());
		val.erase(unique(val.begin(), val.end()), val.end());
		siz = val.size();
		tr.resize(siz + 1);
	}
	void change(int x, int c){
		x = lower_bound(val.begin(), val.end(), x) - val.begin() + 1;
		for(; x <= siz; x += lowbit(x)){
			tr[x] += c;
		}		
	}
	int ask(int x){
		x = lower_bound(val.begin(), val.end(), x) - val.begin();
		int ans = 0;
		for(; x; x -= lowbit(x)){
			ans += tr[x];
		}
		return ans;
	}
}occ;
struct Segment{
	int l, r;
	Bit dat;
}seg[N * 4];
void build(int p, int l, int r){
	seg[p].l = l;
	seg[p].r = r;
	vector<int> dat; dat.clear();
	for(int i = l; i <= r; i++){
		dat.push_back(a[i]);
	}
	seg[p].dat.build(dat);
	if(l == r){
		return ;
	}
	int mid = (l + r) >> 1;
	build(p * 2, l, mid);
	build(p * 2 + 1, mid + 1, r); 
}
void change(int p, int x, int v){
	// if(seg[p].l == seg[p].r && seg[p].l == 3){
		// cerr << "?? " << a[x] << ' ' << v << '\n';
	// }
	seg[p].dat.change(a[x], v);
	if(seg[p].l == seg[p].r) return ;
	int mid = (seg[p].l + seg[p].r) >> 1;
	if(x <= mid){
		change(p * 2, x, v);
	}
	else{
		change(p * 2 + 1, x, v);
	}
}
int ask(int p, int l, int r, int x){
	if(seg[p].l >= l && seg[p].r <= r){
		return seg[p].dat.ask(x);
	}
	int mid = (seg[p].l + seg[p].r) >> 1;
	int val = 0;
	if(l <= mid){
		val += ask(p * 2, l, r, x);
	}
	if(r > mid){
		val += ask(p * 2 + 1, l, r, x);
	}
	return val;
}
int ask_binary(int p, int l, int r, int x, int &pos){
	// cerr << p << ' ' << seg[p].l << ' ' << seg[p].r <<' ' << x << ' ' << pos << '\n';
	if(l > seg[p].r || r < seg[p].l) return 1e9;
	if(seg[p].l >= l && seg[p].r <= r){
		int val = seg[p].dat.ask(x) - seg[p].r + seg[p].l - 1;
		// cerr << seg[p].dat.ask(x) << '\n';
		if(pos + val > 0){
			pos += val;
			return 1e9;
		}
	}
	if(seg[p].l == seg[p].r) {
		return seg[p].r;
	}
	int mid = (seg[p].l + seg[p].r) >> 1;
	int val = 1e9;
	if(l <= mid){
		val = ask_binary(p * 2, l, r, x, pos);
	}
	if(val > seg[p].r && r > mid){
		val = ask_binary(p * 2 + 1, l, r, x, pos);
	}
	return val;
}
struct Quest{
	int l, x, y, id;
};
vector<Quest> que[N];
bool ans[N];
int lst[N];
vector<bool> kapok(int c, int NN, int MM, 
	vector<int> A, vector<int> L, vector<int> R, vector<int> X, vector<int> Y){
	n = NN;
	m = MM;
	for(int i = 1; i <= n; i++){
		a[i] = A[i - 1] + 1;
	}
	build(1, 1, n);
	for(int i = 1; i <= m; i++){
		int l = L[i - 1] + 1;
		int r = R[i - 1] + 1;
		int x = X[i - 1] + 1;
		int y = Y[i - 1] + 1;
		if(x == y) continue;
		if(l == r){
			ans[i] = 1;
			continue;
		}
		if(x != r - l + 2){
			que[r - 1].push_back({l, x, y, i});
		}
		if(y != r - l + 2){
			que[r - 1].push_back({l, y, x, i});
		}
	}

	vector<int> tmp;
	for(int i = 1; i <= n; i++){
		tmp.push_back(a[i]);
	}
	occ.build(tmp);
	// return {0};
	for(int i = 1; i <= n; i++){
		if(lst[a[i]]){
			change(1, lst[a[i]], -1);
		}
		else{
			occ.change(a[i], 1);
		}
		// cerr << i << '\n';
		lst[a[i]] = i;
		change(1, lst[a[i]], 1);
		// cerr << i << '\n';
		for(auto quest: que[i]){
			int l = quest.l, x = quest.x, y = quest.y;
			int r = i;
			if(y != r + 1 - l + 2 && lst[x] > lst[y]) continue;//无论如何都赢不了
			int cnt = x - occ.ask(x) - 1 + ask(1, 1, max(l - 1, lst[x]), x) - max(0, lst[x] - l + 1);
			int pos = (cnt <= 0 || lst[x] == i) ? max(lst[x] + 1, l) : ask_binary(1, max(lst[x] + 1, l), r, x, cnt) + 1;
			int val = (pos > r ? r - l + 3 : min(r - l + 3, a[pos]));
			ans[quest.id] = (y == val);
		}
		// cerr << i << '\n';
	}
	vector<bool> res;
	for(int i = 1; i <= m; i++){
		res.push_back(ans[i]);
	}
	return res;
}
posted @ 2026-07-29 16:15  Air2011  阅读(29)  评论(0)    收藏  举报