ZSTU 4241 圣杯战争(ST表+二分)
题目链接 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; }