1. 程式人生 > >ZSTU 4241 聖杯戰爭(ST表+二分)

ZSTU 4241 聖杯戰爭(ST表+二分)

esp 戰爭 def 最大值 cal continue online 問題 %d

題目鏈接 ZSTU 4241

問題轉化為有很多區間,現在每次給定一個區間求這個區間和之前所有區間中的某一個的交集的最大長度。

強制在線。

首先我們把所有的區間預處理出來。

然後去重(那些被包含的小區間可以去掉),再根據左端點升序排序。

這樣的話這些區間的右端點也是嚴格升序的。

現在對於給定的$[x, y]$

所有區間大概可以分成三類。

1、左端點落在$[1, x - 1]$,對於這類區間查詢右端點最大值即可。

2、右端點落在$[y + 1, n]$,對於這類區間查詢左端點最小值即可。

3、除了上面兩種的其他區間,對於這類區間查詢區間長度最大值即可。

1、2直接利用單調性,二分找到滿足條件的位置即可。

3的話……我選擇了ST表。

最後答案不能超過給定區間的長度。

時間復雜度$O(nlogn)$

#include <bits/stdc++.h>

using namespace std;

#define rep(i, a, b)	for (int i(a); i <= (b); ++i)
#define dec(i, a, b)	for (int i(a); i >= (b); --i)

typedef long long LL;

const int N = 2e5 + 10;

struct node{
	int x, y;
	friend bool operator < (const node &a, const node &b){
		return a.x == b.x ? a.y > b.y : a.x < b.x;
	}
} b[N], c[N];

LL a[N], s[N], p[N];
int pos[N];
int n, m, q;
int T;
int num, cnt, ans;
int f[N][22];
int lg[N];

inline LL calc(int l, int r){ return s[r] - s[l - 1]; }

inline int solve(int l, int r){
	if (l > r) return 0;
	int k = lg[r - l + 1];
	return max(f[l][k], f[r - (1 << k) + 1][k]);
}

void work(){
	rep(i, 1, cnt) f[i][0] = c[i].y - c[i].x + 1;
	rep(j, 1, 20) rep(i, 1, cnt)
		if ((i + (1 << j) - 1) <= cnt) f[i][j] = max(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]);
}

int main(){

	lg[1] = 0; rep(i, 2, 2e5) lg[i] = lg[i >> 1] + 1;

	scanf("%d", &T);
	while (T--){
		scanf("%d%d%d", &n, &m, &q);
		rep(i, 1, n) scanf("%lld", a + i);
		s[0] = 0;
		rep(i, 1, n) s[i] = s[i - 1] + a[i];
		rep(i, 1, m) scanf("%d", pos + i);
		rep(i, 1, m) scanf("%lld", p + i);
		num = 0;
		rep(i, 1, m){
			if (p[i] < a[pos[i]]) continue;
			int l = pos[i], r = n;
			while (l + 1 < r){
				int mid = (l + r) >> 1;
				if (calc(pos[i], mid) <= p[i]) l = mid;
				else r = mid - 1;
			}

			int t;
			if (calc(pos[i], r) <= p[i]) t = r; else t = l;
			++num;
			b[num].x = pos[i], b[num].y = t;
		}

		rep(i, 1, m){
			if (p[i] < a[pos[i]]) continue;
			int l = 1, r = pos[i];
			while (l + 1 < r){
				int mid = (l + r) >> 1;
				if (calc(mid, pos[i]) <= p[i]) r = mid;
				else l = mid + 1;
			}

			int t;
			if (calc(l, pos[i]) <= p[i]) t = l; else t = r;
			++num;
			b[num].x = t, b[num].y = pos[i];
		}
		
		sort(b + 1, b + num + 1);
		cnt = 0;
		for (int i = 1, j; i <= num; ){
			j = i + 1;
			while (j <= num && b[j].y <= b[i].y) ++j;
			c[++cnt] = b[i];
			i = j;
		}

		memset(f, 0, sizeof f);
		work();

		ans = 0;
		while (q--){
			int x, y;
			scanf("%d%d", &x, &y);
			x ^= ans;
			y ^= ans;
			if (x > y) swap(x, y);

			ans = 0;
			int X, Y;
			if (c[1].x >= x) X = 1;
			else{
				int l = 1, r = cnt;
				while (l + 1 < r){
					int mid = (l + r) >> 1;
					if (c[mid].x < x) l = mid; else r = mid - 1;
				}

				if (c[r].x < x){ X = r + 1; ans = max(ans, c[r].y - x + 1);}
				else{ X = l + 1; ans = max(ans, c[l].y - x + 1);}
			}

			if (c[cnt].y <= y) Y = cnt;
			else{
				int l = 1, r = cnt;
				while (l + 1 < r){
					int mid = (l + r) >> 1;
					if (c[mid].y > y) r = mid; else l = mid + 1;
				}

				if (c[l].y > y){ Y = l - 1; ans = max(ans, y - c[l].x + 1);}
				else{ Y = r - 1; ans = max(ans, y - c[r].x + 1); }
			}

			ans = max(ans, solve(X, Y));
			ans = min(ans, y - x + 1);
			printf("%d\n", ans);
		}
	}

	return 0;
}

  

ZSTU 4241 聖杯戰爭(ST表+二分)