Luogu2839 middle - 二分 - 主席树 -

题目链接:https://www.luogu.com.cn/problem/P2839

题解:
考虑二分中位数 \(m\),如果数 \(\geq m\) 就取 1 否则取 -1,那么 \(m\) 是中位数的必要条件是区间和 \(\geq 0\),最大的中位数就是二分出来 \(m\) 的最大值
如果直接枚举 \(m\) 的大小可能不好做,由于本题的中位数一定在 \(b\) 中取到,我们可以直接二分下标,由于需要满足单调性,二分的是排序后的下标
区间和是哪些部分呢?\([a,b)最大后缀 + [b,c] + (c,d]最大前缀\)
如果直接对所有的 \(b_i\) 都用线段树维护对应 0/1 序列的区间和显然会爆炸,考虑使用主席树,每次对应的 \(b_i\) 都由上一个版本继承过来,然后再将 \(b_i\) 对应的 \([i,i]\) 位置的区间和改成 -1,前缀后缀最大和改成 0,代表将这个点修改为 "小于 \(b_i\)"
每次查询的时候,就查一下三段的区间和,然后如果 \(\geq 0\) 就代表中位数 \(\geq x\),这样取到的最大值就是答案

// by SkyRainWind
#include <bits/stdc++.h>
#define mpr make_pair
#define debug() cerr<<"Yoshino\n"
#define pii pair<int,int>
#define pb push_back

using namespace std;

typedef long long ll;
typedef long long LL;

const int inf = 1e9, INF = 0x3f3f3f3f, maxn = 20000+5;

int n;
pii a[maxn];
struct iv{
	int sum, pre, suf;
};
iv operator + (iv a, iv b){
	iv c;
	c.sum = a.sum + b.sum;
	c.pre = max(a.pre, a.sum + b.pre);
	c.suf = max(b.suf, b.sum + a.suf);
	return c;
}

struct segm{
	iv val;
	int ls, rs;
}se[maxn << 5];
int rt[maxn];

int clk;

void upd(int num){
	se[num].val = se[se[num].ls].val + se[se[num].rs].val;
}

void build(int l,int r,int &num){
	num = ++ clk;
	if(l == r){se[num].val=iv{1,1,1};return ;}
	int mid = l+r>>1;
	build(l,mid,se[num].ls);
	build(mid+1,r,se[num].rs);
	se[num].val = iv{r-l+1,r-l+1,r-l+1};
}

void update(int pre,int &num,int l,int r,int to){
	num = ++clk;
	se[num].ls = se[pre].ls, se[num].rs = se[pre].rs;
	if(l == r){
		se[num].val = iv{-1,0,0};
		return ;
	}
	int mid = l+r>>1;
	if(to <= mid)update(se[pre].ls,se[num].ls,l,mid,to);
	else update(se[pre].rs,se[num].rs,mid+1,r,to);
	se[num].val = se[se[num].ls].val + se[se[num].rs].val;
}

iv query(int l,int r,int ql,int qr,int num){
	if(ql <= l && r <= qr){
		return se[num].val;
	}
	int mid = l+r>>1;
	if(qr <= mid)return query(l,mid,ql,qr,se[num].ls);
	else if(ql > mid)return query(mid+1,r,ql,qr,se[num].rs);
	else return query(l,mid,ql,qr,se[num].ls) + query(mid+1,r,ql,qr,se[num].rs);
}

signed main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%d",&a[i].first), a[i].second = i;
	build(1,n,rt[1]);
	sort(a+1,a+n+1,[&](pii a,pii b){
		return a.first < b.first;
	});
	for(int i=2;i<=n;i++)
		update(rt[i-1],rt[i],1,n,a[i-1].second);

	int qu;scanf("%d",&qu);
	int lst = 0;
	while(qu --){
		vector<int>c(4);
		for(int i=0;i<4;i++)scanf("%d",&c[i]), c[i] = (c[i] + lst)%n + 1;
		sort(c.begin(), c.end());

		int l=1, r=n, ans;
		while(l <= r){
			int mid = l+r>>1;
			iv s1 = query(1,n,c[0],c[1]-1,rt[mid]);
			iv s2 = query(1,n,c[1],c[2],rt[mid]);
			iv s3 = query(1,n,c[2]+1,c[3],rt[mid]);

			if(s1.suf + s2.sum + s3.pre >= 0)ans = mid, l = mid+1;
			else r = mid-1;
		}
		printf("%d\n",lst = a[ans].first);
	}

	return 0;
}

posted @ 2023-05-15 20:27  SkyRainWind  阅读(16)  评论(0编辑  收藏  举报