P2839 [国家集训队]middle(主席树+二分)

这篇博客介绍了如何运用主席树解决在动态区间修改和查询最大值的问题,具体涉及到二分查找、离散化以及区间贡献的计算。通过案例解释了在国家集训队的题目中,如何将数组元素转换为0和1,并通过主席树维护区间和,以判断是否存在满足条件的划分。最后,博主给出了完整的C++代码实现。
摘要由CSDN通过智能技术生成

P2839 [国家集训队]middle

我现在只觉得这题很nb

可以考虑二分答案,把中间小于 m i d mid mid的数设为 − 1 -1 1,其他的设为 1 1 1,然后查看区间和是否大于 0 0 0,若大于等于 0 0 0,则说明可取,反之,不可取

由于区间端点不是固定的,左端点在一个区间内,右端点在另一个区间内,那么最大前缀就是

l m a x = m a x ( l m a l , s u m l + l m a r ) lma_x=max(lma_l,sum_l+lma_r) lmax=max(lmal,suml+lmar)

那么最大的后缀也是同理

r m a x = m a x ( r m a r , s u m r + r m a l ) rma_x=max(rma_r,sum_r+rma_l) rmax=max(rmar,sumr+rmal)

由于左端点在 [ a , b ] [a,b] [a,b]内,右端点在 [ c , d ] [c,d] [c,d]内,要查询的区间必然存在 [ b , c ] [b,c] [b,c],所以大于等于 0 0 0时才是可取的

为了确保二分的数一定是正确的(即原数组中出现过),可以采取先离散化,后二分的方式,这样就确保二分是一定正确的了

所以现在,问题的难点在于如何把区间的数变为 0 0 0 1 1 1,考虑从 − 1 -1 1 1 1 1的时候,仅当 m i d mid mid m i d < b i mid<b_i mid<bi,变到 m i d > = b i mid>=b_i mid>=bi b i b_i bi是离散化后的数组)时,才会从 − 1 -1 1变成 1 1 1,每个数的贡献仅仅只会被改变一次。

由于这些数是可以共用的,所以可以使用主席树

#include <bits/stdc++.h>
#define inf 0x7fffffff
#define ll long long
#define int long long
//#define double long double
#define re register int
#define void inline void
#define eps 1e-8
//#define mod 1e9+7
#define ls(p) p<<1
#define rs(p) p<<1|1
#define pi acos(-1.0)
#define pb push_back
#define P pair < int , int >
#define mk make_pair
using namespace std;
const int mod=1e9+7;
const int M=1e8+5;
const int N=1e5+5;//?????????? 4e8
int n,q,m;
int a[N],b[N];
struct node
{
	int l,r;
	int sum,lma,rma; 
}e[N*80];
int rt[N],tot;
int aa,bb,cc,dd;
void push(int p)
{
	e[p].sum=e[e[p].l].sum+e[e[p].r].sum;
	e[p].lma=max(e[e[p].l].lma,e[e[p].l].sum+e[e[p].r].lma);
	e[p].rma=max(e[e[p].r].rma,e[e[p].r].sum+e[e[p].l].rma);
}
void bulid(int &p,int l,int r)
{
	p=++tot;
	if(l==r)
	{
		e[p].lma=e[p].sum=e[p].rma=-1;
		return;
	}
	int mid=(l+r)>>1;
	bulid(e[p].l,l,mid);bulid(e[p].r,mid+1,r);
	push(p);
}
void insert(int &p,int pre,int l,int r,int pos)
{
	e[++tot]=e[pre];
	p=tot;
	if(l==r)
	{
		e[p].sum=e[p].lma=e[p].rma=1;
		return;
	}
	int mid=(l+r)>>1;
	if(pos<=mid)  insert(e[p].l,e[pre].l,l,mid,pos);
	else  insert(e[p].r,e[pre].r,mid+1,r,pos);
	push(p);
}
node ask(int p,int l,int r,int L,int R)
{
	if(L<=l&&r<=R)  return e[p];
	int mid=(l+r)>>1;
	if(L>mid)  return ask(e[p].r,mid+1,r,L,R);
	else if(R<=mid)  return ask(e[p].l,l,mid,L,R);
	else
	{
		node ans,lson,rson;
		lson=ask(e[p].l,l,mid,L,R);
		rson=ask(e[p].r,mid+1,r,L,R);
		ans.sum=(lson.sum+rson.sum);
		ans.lma=max(lson.lma,lson.sum+rson.lma);
		ans.rma=max(rson.rma,rson.sum+lson.rma);
		return ans;
	} 
}
bool check(int mid)
{
	int res=0;
	node ans=ask(rt[mid],1,n,aa,bb);
	res+=ans.rma;
	if(bb+1<=cc-1)  ans=ask(rt[mid],1,n,bb+1,cc-1),res+=ans.sum;
	ans=ask(rt[mid],1,n,cc,dd);res+=ans.lma;
	return res>=0;
}
bool cmp(int x,int y)
{
	return a[x]<a[y];
}
int ans;
void solve()
{
	cin>>n;
	for(re i=1;i<=n;i++)  scanf("%lld",&a[i]),b[i]=i;
	sort(b+1,b+n+1,cmp);
	bulid(rt[n+1],1,n);
	for(re i=n;i>=1;i--)  insert(rt[i],rt[i+1],1,n,b[i]);
	cin>>q;
	while(q--)
	{
		scanf("%lld%lld%lld%lld",&aa,&bb,&cc,&dd);
		aa=(aa+ans)%n+1,bb=(bb+ans)%n+1;
		cc=(cc+ans)%n+1,dd=(dd+ans)%n+1;
		int t[5]={0,aa,bb,cc,dd};
		sort(t+1,t+4+1);
//		for(re i=1;i<=4;i++)  cout<<t[i]<<" ";puts(""); 
		aa=t[1],bb=t[2],cc=t[3],dd=t[4];
		int l=1,r=n,cnt;
		while(l<=r)
		{
			int mid=(l+r)>>1;
			if(check(mid))  cnt=mid,l=mid+1;
			else  r=mid-1;
		}
		ans=a[b[cnt]];
		printf("%lld\n",ans);
	}
}
signed main()
{
    int T=1;
//    cin>>T;
    for(int index=1;index<=T;index++)
    {
//    	printf("Case %d:\n",index);
        solve();
//        puts("");
    }
    return 0;
}
/*


1
6 5
0 0 0 122 499 8888




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值