1. 程式人生 > 其它 >【luogu CF1039E】Summer Oenothera Exhibition(貪心)(根號分治)(LCT)

【luogu CF1039E】Summer Oenothera Exhibition(貪心)(根號分治)(LCT)

Summer Oenothera Exhibition

題目連結:luogu CF1039E

題目大意

給你一個數組,然後每次問你一個 k,問你能把陣列最少分成多少段,使得每一段的極差不超過 k。

思路

首先考慮對於一次詢問怎麼做。

考慮到一個貪心的想法,就是一個區間能往右擴一點是一點。
不難發現這樣是正確的。

然後你考慮是否可以有一些東西優化這個過程。
那你每個地方你可以通過二分+區間求極差【可以用 ST 表】得到它作為一段的左邊的話下一個段應該從哪裡開始 \(nxt_i\)

然後你會發現如果我們把它連邊,它可以相當於要求從 \(1\)\(n+1\) 點的路徑長度。
那這個加邊的我們可以用 DP 一下得到。
那考慮有沒有一些東西方便擴充套件的,那 \(k\)

修改了邊可能會變,那我們也可以用 LCT 來維護。

但是你每次可能都要更改每個邊的 \(nxt_i\) 導致修改次數是 \(n^2\) 複雜度是 \(n^2\log n\) 甚至不如暴力。
那我們考慮根號分治。

你會發現暴力跳的問題就是每次跳的距離太短,LCT 跳的問題就是修改的次數太多了。
那我們 \(\leqslant B\) 的我們用 LCT,那這樣每個數的 \(nxt_i\) 頂多修改 \(B\) 次。
然後大於的用暴力做,那每次這麼跳的次數是 \(\dfrac{n}{B}\)
\(B=\sqrt{n}\) 就可以做到 \(O(n\sqrt{n}\log n)\)

然後接著簡單講講實現,考慮記錄每個每個位置記錄它是怎麼跳的。
然後 LCT 的部分就不說了,說說怎麼修改 \(nxt_i\)


考慮算出每個位置下一次修改是在那個詢問,然後放進這個詢問的 vector 裡面。
然後每次看詢問之間先把 vector 裡面的搞出來修改了。

然後更新就是暴力加出位置,然後再用 lower_bound 得到下次在哪個詢問。

程式碼

#include<cmath>
#include<cstdio>
#include<vector>
#include<iostream>
#include<algorithm>

using namespace std;

const int N = 1e5 + 100;
const int B = 230;
struct node {
	int id, k;
}s[N];
int n, w, q, a[N], log2_[N], nxt[N];
int maxn[N][18], minn[N][18], xl[N];
int ans[N];
vector <int> st[N];
bool bl[N];

bool cmp(node x, node y) {
	return x.k < y.k;
}

int clac(int l, int r) {
	if (r == n + 1) return 2e9; 
	int k = log2_[r - l + 1];
	return max(maxn[l][k], maxn[r - (1 << k) + 1][k]) - min(minn[l][k], minn[r - (1 << k) + 1][k]);
}

int get_nxt(int now, int k) {
	int l = now, r = n, re;
	while (l <= r) {
		int mid = (l + r) >> 1;
		if (clac(now, mid) <= k) re = mid, l = mid + 1;
			else r = mid - 1;
	}
	return re + 1;
}

struct LCT {
	int sz[N], ls[N], rs[N], fa[N];
	
	bool nrt(int x) {return ls[fa[x]] == x || rs[fa[x]] == x;}
	bool lrs(int x) {return ls[fa[x]] == x;}
	void up(int x) {sz[x] = sz[ls[x]] + sz[rs[x]] + 1;}
	
	void rotate(int x) {
		int y = fa[x], z = fa[y];
		int b = lrs(x) ? rs[x] : ls[x];
		if (z && nrt(y)) (lrs(y) ? ls[z] : rs[z]) = x;
		if (lrs(x)) rs[x] = y, ls[y] = b;
			else ls[x] = y, rs[y] = b;
		fa[x] = z; fa[y] = x;
		if (b) fa[b] = y;
		up(y); up(x);
	}
	
	void Splay(int x) {
		while (nrt(x)) {
			if (nrt(fa[x])) {
				if (lrs(x) == lrs(fa[x])) rotate(fa[x]);
					else rotate(x);
			}
			rotate(x);
		}
	}
	
	void access(int x) {
		int lst = 0;
		for (; x; lst = x, x = fa[x]) {
			Splay(x);
			
			rs[x] = lst;
			up(x);
		}
	}
	
	int find_root(int x) {
		access(x); Splay(x);
		while (ls[x]) x = ls[x];
		Splay(x); 
		return x;
	}
	
	void link(int x, int y) {
		access(x); fa[x] = y;
	}
	
	void cut(int x, int y) {
		access(x); Splay(y);
		fa[x] = 0; rs[y] = 0;
		up(y);
	}
}T;

int main() {
	scanf("%d %d %d", &n, &w, &q);
	for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
	for (int i = 1; i <= q; i++) {
		scanf("%d", &s[i].k); s[i].k = w - s[i].k;
		s[i].id = i;
	}
	sort(s + 1, s + q + 1, cmp);
	for (int i = 1; i <= q; i++) xl[i] = s[i].k;
	
	for (int i = 1; i <= n; i++) maxn[i][0] = minn[i][0] = a[i];
	log2_[0] = -1; for (int i = 1; i <= n; i++) log2_[i] = log2_[i >> 1] + 1;
	for (int i = 1; i <= 17; i++)
		for (int j = 1; j + (1 << i) - 1 <= n; j++) {
			maxn[j][i] = max(maxn[j][i - 1], maxn[j + (1 << (i - 1))][i - 1]);
			minn[j][i] = min(minn[j][i - 1], minn[j + (1 << (i - 1))][i - 1]);
		}
	
	for (int i = 1; i <= n; i++) st[1].push_back(i), nxt[i] = i;
	for (int i = 1; i <= q; i++) {
		for (int j = 0; j < st[i].size(); j++) {
			int now = st[i][j], fr = nxt[now] + 1;
			T.cut(now, nxt[now]);
			while (fr <= n && fr - now <= B && clac(now, fr) <= s[i].k) fr++;
			if (fr - now > B) bl[now] = 1;
				else {
					int to = lower_bound(xl + 1, xl + q + 1, clac(now, fr)) - xl;
					nxt[now] = fr; T.link(now, nxt[now]);
					st[to].push_back(now);
				}
		}
		for (int j = 1; j <= n; j = get_nxt(j, s[i].k)) {
			if (!bl[j]) {
				T.access(j); T.Splay(j); ans[s[i].id] += T.sz[j] - 1;
				j = T.find_root(j);
			}
			if (j == n + 1) break;
			ans[s[i].id]++;
		}
	}
	
	for (int i = 1; i <= q; i++) printf("%d\n", ans[i] - 1);
	
	return 0;
}