bzoj 2653 middle 二分答案+主席树

题面

题目传送门

解法

其实这道题严格上说并不是主席树,而是可持久化线段树。

  • 显然答案满足单调性,假设我们当前二分到的是 m i d mid mid,那么对应的中位数即为 a [ m i d ] a[mid] a[mid]( a [ m i d ] a[mid] a[mid]表示从小到大排完序之后的第 m i d mid mid小,不是这个序列中的数显然可以不用考虑)。然后将小于它的数变成-1,其他数变成1。然后对于 [ a , b ] , [ c , d ] [a,b],[c,d] [a,b],[c,d],中间 [ b , c ] [b,c] [b,c]的部分是一定要选的,其他要选择的部分就是 [ a , b − 1 ] [a,b-1] [a,b1]的最大后缀和与 [ c + 1 , d ] [c+1,d] [c+1,d]的最大前缀和。如果选择的数的和的最大值非负,那么 m i d mid mid合法。
  • 然后就发现这个做法的时间复杂度为 O ( n q log ⁡ n ) O(nq\log n) O(nqlogn)可能还会更劣,显然是不行的。
  • 那么我们就考虑如何优化这个做法。
  • 考虑对于每一个数都建出一棵线段树,比它小的数为-1,不小于它的数为1,,然后维护每一段区间的最大前缀和,最大后缀和与区间和。
  • 那么我们每一次在二分的时候只要查询对应的线段树,查询相关信息即可。
  • 时间复杂度: O ( q log ⁡ 2 n ) O(q\log^2n) O(qlog2n),但是空间好像是爆炸了诶……
  • 然后我们发现, i i i i − 1 i-1 i1对应的线段树上唯一的不同就是原来 a [ i − 1 ] a[i-1] a[i1]对应的数的位置在 i i i这棵线段树上从1变成-1了。那么因为只有这一个点发生了改动,所以我们可以直接使用可持久化线段树来维护这个过程就可以了。
  • 时间复杂度: O ( q log ⁡ 2 n ) O(q\log^2n) O(qlog2n),空间复杂度: O ( n log ⁡ n ) O(n\log n) O(nlogn),足以通过此题。

代码

#include <bits/stdc++.h>
#define PI pair <int, int>
#define mp make_pair
#define inf 1 << 30
#define N 20010
using namespace std;
template <typename node> void read(node &x) {
    x = 0; int f = 1; char c = getchar();
    while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
    while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
int max(int x, int y) {return x > y ? x : y;}
struct Info {
	int val, id;
	bool operator < (const Info &a) const {return val < a.val;}
} a[N];
struct SegmentTree {
    struct Node {int lc, rc, sum, mxl, mxr;} t[N * 35];
    int tot;
    int mxl(int k) {return t[k].mxl;}
    int mxr(int k) {return t[k].mxr;}
    int sum(int k) {return t[k].sum;}
    void update(int k) {
        int lc = t[k].lc, rc = t[k].rc;
        t[k].sum = sum(lc) + sum(rc);
        t[k].mxl = max(sum(lc) + mxl(rc), mxl(lc));
        t[k].mxr = max(sum(rc) + mxr(lc), mxr(rc));
    }
    void build(int &k, int l, int r) {
        k = ++tot;
        if (l == r) return t[k] = (Node) {0, 0, 1, 1, 1}, void();
        int mid = (l + r) >> 1;
        build(t[k].lc, l, mid), build(t[k].rc, mid + 1, r);
        update(k);
    }
    void ins(int &cur, int k, int l, int r, int x) {
        cur = ++tot; t[cur] = t[k];
        if (l == r) return t[cur].sum = t[cur].mxl = t[cur].mxr = -1, void();
		int mid = (l + r) >> 1;
        if (x <= mid) ins(t[cur].lc, t[k].lc, l, mid, x);
            else ins(t[cur].rc, t[k].rc, mid + 1, r, x);
        update(cur);
    }
    PI queryl(int k, int l, int r, int L, int R) {
    	if (L > R) return mp(0, 0);
        if (L <= l && r <= R) return mp(t[k].sum, t[k].mxl);
        int mid = (l + r) >> 1;
        if (R <= mid) return queryl(t[k].lc, l, mid, L, R);
        if (L > mid) return queryl(t[k].rc, mid + 1, r, L, R);
        PI tx = queryl(t[k].lc, l, mid, L, mid), ty = queryl(t[k].rc, mid + 1, r, mid + 1, R), ret;
        ret.first = tx.first + ty.first, ret.second = max(tx.first + ty.second, tx.second);
        return ret;
    }
    PI queryr(int k, int l, int r, int L, int R) {
    	if (L > R) return mp(0, 0);
        if (L <= l && r <= R) return mp(t[k].sum, t[k].mxr);
        int mid = (l + r) >> 1;
        if (R <= mid) return queryr(t[k].lc, l, mid, L, R);
        if (L > mid) return queryr(t[k].rc, mid + 1, r, L, R);
        PI tx = queryr(t[k].lc, l, mid, L, mid), ty = queryr(t[k].rc, mid + 1, r, mid + 1, R), ret;
        ret.first = tx.first + ty.first, ret.second = max(ty.first + tx.second, ty.second);
        return ret;
    }
} T;
int rt[N];
int main() {
    int n; read(n);
    for (int i = 1; i <= n; i++) read(a[i].val), a[i].id = i;
    sort(a + 1, a + n + 1);
    T.build(rt[1], 1, n);
    for (int i = 2; i <= n; i++) T.ins(rt[i], rt[i - 1], 1, n, a[i - 1].id);
    int q, las = 0; read(q);
    while (q--) {
    	int tx[4];
    	for (int i = 0; i < 4; i++) read(tx[i]), tx[i] = (tx[i] + las) % n + 1;
    	sort(tx, tx + 4);
		int l1 = tx[0], r1 = tx[1], l2 = tx[2], r2 = tx[3], l = 1, r = n, ans;
		while (l <= r) {
			int mid = (l + r) >> 1, sum = 0;
			PI tx = T.queryr(rt[mid], 1, n, l1, r1 - 1), ty = T.queryl(rt[mid], 1, n, r1, l2), tz = T.queryl(rt[mid], 1, n, l2 + 1, r2);
			sum = ty.first + max(0, tx.second) + max(0, tz.second);
			if (sum >= 0) ans = mid, l = mid + 1; else r = mid - 1;
		}
		las = a[ans].val; cout << a[ans].val << "\n";
	}
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值