[主席树] CTSC Middle

传送门

首先一眼望去主席树,但是左右端点在区间不会
刚做了一道题也是处理范围问题,用数位DPqwq(冯巨的那篇blog我想看好久了还是咕的)

权值线段树的想法
区间内权值的一半是中位数所在的位置
左>右,往左走;左<右,向右走
但是这种方法没办法进行不确定的区间的搜索( Θ ( n 2 ) \Theta(n^2) Θ(n2)

考虑另一种中位数求法:
二分答案
小于mid的数赋为-1,大于等于mid的数赋为1
区间和>0往右走,区间和<0往左走
也符合主席树的方法

注意一下题意,n%2==0时取的是中间两个里面靠后的一个
所以区间和=0要向右走

考虑这种不确定的区间(对于一个mid,整个序列里面都是-1,1,1,-1,...
[ b + 1 , c − 1 ] [b+1,c-1] [b+1,c1]一定取得到,全取
[ a , b ] , [ c , d ] [a,b],[c,d] [a,b],[c,d]的最大后缀和最大前缀
这一堆加起来决定mid变大还是变小

mid从小到大建主席树

debug
在这里插入图片描述
特别小心更新的时候,有可能会让前面的更新被未更新的pre覆盖掉
写离散化在用这种方法要写挂
于是换种方式:以排序代离散化,每个位置建主席树

ACcode

#include<bits/stdc++.h>
using namespace std;
#define in Read()
int in{
	int i=0,f=1;char ch=0;
	while(!isdigit(ch)&&ch!='-') ch=getchar();
	if(ch=='-') ch=getchar(),f=-1;
	while(isdigit(ch)) i=(i<<1)+(i<<3)+ch-48,ch=getchar();
	return i*f;
}

const int N=2e4+5;
int n,q,sz;
struct Data{
	int val,pos;
}a[N];
struct SGT{
	int lch,rch,lmx,rmx,sum;
	#define lch(x) tre[(x)].lch
	#define rch(x) tre[(x)].rch
	#define lmx(x) tre[(x)].lmx
	#define rmx(x) tre[(x)].rmx
	#define sum(x) tre[(x)].sum
}tre[N<<5];
int qur[10],l1,l2,r1,r2;
int rt[N];

bool cmp(const Data u,const Data v){
	return u.val<v.val;
}

void push_up(int p){
	sum(p)=sum(lch(p))+sum(rch(p));
	lmx(p)=max(lmx(lch(p)),sum(lch(p))+lmx(rch(p)));
	rmx(p)=max(rmx(rch(p)),sum(rch(p))+rmx(lch(p)));
	return;
}

void build(int &p,int l,int r){
	p=++sz;
	if(l==r){
		lmx(p)=rmx(p)=sum(p)=1;
		return;
	}
	int mid=l+r>>1;
	build(lch(p),l,mid);
	build(rch(p),mid+1,r);
	push_up(p);
	return;
}

void update(int &p,int pre,int l,int r,int loc){
	p=++sz;
	tre[p]=tre[pre];
	if(l==r){
		sum(p)=lmx(p)=rmx(p)=-1;
		return;
	}
	int mid=l+r>>1;
	if(loc<=mid) update(lch(p),lch(pre),l,mid,loc);
	else update(rch(p),rch(pre),mid+1,r,loc);
	push_up(p);
	return;
}

int qsm(int p,int l,int r,int L,int R){
	if(L<=l&&r<=R) return sum(p);
	int mid=l+r>>1;
	if(L>mid) return qsm(rch(p),mid+1,r,L,R);
	if(R<=mid) return qsm(lch(p),l,mid,L,R);
	return qsm(lch(p),l,mid,L,R)+qsm(rch(p),mid+1,r,L,R);
}

SGT merge(SGT l,SGT r){
	SGT res;
	res.sum=l.sum+r.sum;
	res.lmx=max(l.lmx,l.sum+r.lmx);
	res.rmx=max(r.rmx,r.sum+l.rmx);
	return res;
}

SGT qmx(int p,int l,int r,int L,int R){
	if(L<=l&&r<=R) return tre[p];
	int mid=l+r>>1;
	if(L>mid) return qmx(rch(p),mid+1,r,L,R);
	if(R<=mid) return qmx(lch(p),l,mid,L,R);
	return merge(qmx(lch(p),l,mid,L,R),qmx(rch(p),mid+1,r,L,R));
}

bool check(int x){
	int res=0;
	if(l2+1<=r1-1) res+=qsm(rt[x],1,n,l2+1,r1-1);
	res+=qmx(rt[x],1,n,r1,r2).lmx;
	res+=qmx(rt[x],1,n,l1,l2).rmx;
	return res>=0;
}

void debug(int u,int l,int r){
	if(l==r){cout<<sum(u)<<" ";return;}
	int mid=l+r>>1;
	debug(lch(u),l,mid);debug(rch(u),mid+1,r);
}

int main(){
	n=in;
	for(int i=1;i<=n;++i){
		a[i].val=in;
		a[i].pos=i;
	}
	sort(a+1,a+n+1,cmp);
	
	build(rt[1],1,n);
	
	for(int i=1;i<=n;++i)
		update(rt[i+1],rt[i],1,n,a[i].pos);
	
	q=in;
	int lst=0;
	for(int i=1;i<=q;++i){
		qur[0]=(lst+in)%n;qur[1]=(lst+in)%n;
		qur[2]=(lst+in)%n;qur[3]=(lst+in)%n;
		sort(qur,qur+4);
		l1=qur[0]+1;l2=qur[1]+1;
		r1=qur[2]+1;r2=qur[3]+1;
		int l=1,r=n,ans=1;
		while(l<=r){
			int mid=l+r>>1;
			if(check(mid)) ans=mid,l=mid+1;
			else r=mid-1;
		}
		lst=a[ans].val;
		printf("%d\n",lst);
	}
	return 0;
}

黑色经验:主席树Debug输出底层数据

void debug(int p,int l,int r){
	if(l==r){cout<<sum(p)<<" ";return;}
	int mid=l+r>>1;
	debug(lch(p),l,mid);
	debug(rch(p),mid+1,r);
	return;
}

感谢@gigo巨佬的帮助

最后发现我build的时候没有p=++sz

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值