Luogu2839 middle - 二分 - 主席树 -

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

题解:
考虑二分中位数 m,如果数 m 就取 1 否则取 -1,那么 m 是中位数的必要条件是区间和 0,最大的中位数就是二分出来 m 的最大值
如果直接枚举 m 的大小可能不好做,由于本题的中位数一定在 b 中取到,我们可以直接二分下标,由于需要满足单调性,二分的是排序后的下标
区间和是哪些部分呢?[a,b)+[b,c]+(c,d]
如果直接对所有的 bi 都用线段树维护对应 0/1 序列的区间和显然会爆炸,考虑使用主席树,每次对应的 bi 都由上一个版本继承过来,然后再将 bi 对应的 [i,i] 位置的区间和改成 -1,前缀后缀最大和改成 0,代表将这个点修改为 "小于 bi"
每次查询的时候,就查一下三段的区间和,然后如果 0 就代表中位数 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 @   SkyRainWind  阅读(22)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 记一次.NET内存居高不下排查解决与启示
点击右上角即可分享
微信分享提示