CF484E Sign on Fence
主席树 + 二分答案。
不容易想到二分答案转化为判定可行性的问题,假设每一次询问的区间是$[x, y]$,长度为$k$,那么假设当前二分到$mid$,我们把原序列中所有大于等于$mid$的值都记一个$1$,其他的位置记为$0$,那么我们看一看$[x, y]$这个区间中最长连续的$1$的个数是不是超过了$k$。这个最长连续$1$的个数可以用线段树维护,维护的方法就是 【Luogu 2572 [SCOI2010]序列操作】这个题的弱化版。 戳这里
那这样我们考虑对于每一个不同的$a_i$建立线段树,发现每一棵线段树有很多结点可以从上一个版本过继下来,所以可持久化之后就变成了一个主席树。
我用的方法是直接把所有的$a_i$离散化,然后用$st$表维护一下每一个区间出现的最大的数和最小的数方便二分。
写得很冗长。
时间复杂度$O(qlog^{2}n)$。
Code:
#include <cstdio> #include <cstring> #include <cmath> #include <algorithm> using namespace std; const int N = 1e5 + 5; const int Lg = 20; const int inf = 1 << 30; int n, qn, maxn = 0, a[N]; int v[N], pos[N], len[N], st[2][N][Lg]; struct Item { int val, id; } b[N], in[N]; bool cmp(const Item &x, const Item &y) { if(x.val != y.val) return x.val < y.val; else return x.id < y.id; } inline void read(int &X) { X = 0; char ch = 0; int op = 1; for(; ch > '9' || ch < '0'; ch = getchar()) if(ch == '-') op = -1; for(; ch >= '0' && ch <= '9'; ch = getchar()) X = (X << 3) + (X << 1) + ch - 48; X *= op; } inline int max(int x, int y) { return x > y ? x : y; } inline int max3(int x, int y, int z) { return max(max(x, y), z); } inline int min(int x, int y) { return x > y ? y : x; } inline void chkMax(int &x, int y) { if(y > x) x = y; } inline void discrete() { sort(in + 1, in + 1 + n, cmp); for(int cnt = 0, i = 1; i <= n; i++) { if(in[i].val != in[i - 1].val) ++cnt; a[in[i].id] = cnt; v[cnt] = in[i].val; chkMax(maxn, cnt); } } namespace PSegT { struct Node { int lc, rc, sum, maxSum, lmax, rmax; inline void init() { lc = rc = sum = maxSum = 0; lmax = rmax = 0; } } s[N * 80]; int root[N], nodeCnt = 0; #define mid ((l + r) >> 1) #define lc(p) s[p].lc #define rc(p) s[p].rc #define sum(p) s[p].sum #define lmax(p) s[p].lmax #define rmax(p) s[p].rmax #define maxSum(p) s[p].maxSum inline void up(int p, int l, int r) { if(!p) return; maxSum(p) = max3(maxSum(lc(p)), maxSum(rc(p)), lmax(rc(p)) + rmax(lc(p))); sum(p) = sum(lc(p)) + sum(rc(p)); lmax(p) = lmax(lc(p)); if(sum(lc(p)) == mid - l + 1) chkMax(lmax(p), sum(lc(p)) + lmax(rc(p))); rmax(p) = rmax(rc(p)); if(sum(rc(p)) == r - mid) chkMax(rmax(p), sum(rc(p)) + rmax(lc(p))); } void build(int &p, int l, int r) { p = ++nodeCnt; if(l == r) { sum(p) = lmax(p) = rmax(p) = maxSum(p) = 1; return; } build(lc(p), l, mid); build(rc(p), mid + 1, r); up(p, l, r); } void ins(int &p, int l, int r, int x, int pre) { s[p = ++nodeCnt] = s[pre]; if(l == r) { sum(p) = lmax(p) = rmax(p) = maxSum(p) = 0; return; } if(x <= mid) ins(lc(p), l, mid, x, lc(pre)); else ins(rc(p), mid + 1, r, x, rc(pre)); up(p, l, r); } int go(int p, int l, int r, int x) { if(l == x && x == r) return s[p].sum; if(x <= mid) return go(lc(p), l, mid, x); else return go(rc(p), mid + 1, r, x); } int query(int p, int l, int r, int x, int y) { if(x <= l && y >= r) return maxSum(p); int res = 0, lmax = 0, rmax = 0; if(x <= mid) lmax = query(lc(p), l, mid, x, y); if(y > mid) rmax = query(rc(p), mid + 1, r, x, y); if(x <= mid && y > mid) { int ln = min(mid - x + 1, rmax(lc(p))), rn = min(y - mid, lmax(rc(p))); res = ln + rn; } return max3(lmax, rmax, res); } #undef mid #undef lc #undef rc #undef sum #undef lmax #undef rmax #undef maxSum } using namespace PSegT; inline bool chk(int x, int y, int k, int mid) { int res = query(root[pos[mid]], 1, n, x, y); return res >= k; } inline int stQuery(int type, int x, int y) { int k = len[y - x + 1]; if(!type) return max(st[0][x][k], st[0][y - (1 << k) + 1][k]); else return min(st[1][x][k], st[1][y - (1 << k) + 1][k]); } inline void solve(int x, int y, int k) { int ln = stQuery(1, x, y), rn = stQuery(0, x, y), mid, res; for(; ln <= rn; ) { mid = (ln + rn) / 2; if(chk(x, y, k, mid)) res = mid, ln = mid + 1; else rn = mid - 1; } printf("%d\n", v[res]); } int main() { read(n); for(int i = 1; i <= n; i++) { read(a[i]); in[i].val = a[i], in[i].id = i; } discrete(); /* for(int i = 1; i <= n; i++) printf("%d ", a[i]); printf("\n"); */ for(int i = 1; i <= n; i++) { b[i].id = i, b[i].val = a[i]; st[0][i][0] = st[1][i][0] = a[i], len[i] = log2(i); } for(int j = 1; j <= 18; j++) for(int i = 1; i + (1 << j) - 1 <= n; i++) { st[0][i][j] = max(st[0][i][j - 1], st[0][i + (1 << (j - 1))][j - 1]); st[1][i][j] = min(st[1][i][j - 1], st[1][i + (1 << (j - 1))][j - 1]); } sort(b + 1, b + 1 + n, cmp); build(root[1], 1, n); int ed[N]; for(int i = 2; i <= n + 1; i++) { ins(root[i], 1, n, b[i - 1].id, root[i - 1]); if(b[i].val != b[i - 1].val) ed[b[i - 1].val] = i - 1; } /* for(int i = 1; i <= maxn; i++) printf("%d ", ed[i]); printf("\n"); */ for(int i = 1; i <= maxn; i++) pos[i] = ed[i - 1] + 1; /* for(int i = 1; i <= maxn; i++) printf("%d ", v[i]); printf("\n"); */ /* for(int i = 1; i <= maxn; i++) printf("%d ", pos[i]); printf("\n"); for(int i = 1; i <= maxn; i++) { printf("%d: ", i); for(int j = 1; j <= n; j++) printf("%d ", go(root[pos[i]], 1, n, j)); printf("\n"); } */ read(qn); for(int x, y, k; qn--; ) { read(x), read(y), read(k); solve(x, y, k); } return 0; }