洛谷 P3567 [POI2014]KUR-Couriers (主席树+树上二分)

题目链接

题目大意

image.png

解题思路

较简单的一道主席树题
每次询问时,先处理处当前的区间,然后在主席树上二分
设当前二分区间为 [ l , r ] [l,r] [l,r],且满足要求的最少出现次数为 k k k k = ⌊ r − l + 1 2 ⌋ + 1 k=\lfloor \frac{r-l+1}{2} \rfloor +1 k=2rl+1+1)。

  • 如果 [ l , r ] [l,r] [l,r] 左右两半的值都小于 k k k:直接退出并输出 0 0 0
  • 否则,左右两半中一定有且仅有一半的值大于等于 k k k:找到那一半并继续二分
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int Maxn=500000+10,Maxm=11000000+10;
int sum[Maxm],ls[Maxm],rs[Maxm];
int a[Maxn],root[Maxn];
int n,m,idcnt;
inline int read()
{
    
    
	int s=0,w=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
    
    if(ch=='-')w=-1;ch=getchar();}
	while(ch>='0' && ch<='9')s=(s<<3)+(s<<1)+(ch^48),ch=getchar();
	return s*w;
}
void modify(int &x,int y,int l,int r,int pos)
{
    
    
	x=++idcnt;
	ls[x]=ls[y];
	rs[x]=rs[y];
	sum[x]=sum[y]+1;
	if(l==r)return;
	int mid=(l+r)>>1;
	if(pos<=mid)modify(ls[x],ls[y],l,mid,pos);
	else modify(rs[x],rs[y],mid+1,r,pos);
}
int query(int x,int y,int l,int r,int v)
{
    
    
	if(sum[x]-sum[y]<v)return 0;
	if(l==r)return l;
	int mid=(l+r)>>1,ret=0;
	ret=query(ls[x],ls[y],l,mid,v);
	ret=max(ret,query(rs[x],rs[y],mid+1,r,v));
	return ret;
}
int main()
{
    
    
//	freopen("in.txt","r",stdin);
	n=read(),m=read();
	for(int i=1;i<=n;++i)
	a[i]=read();
	for(int i=1;i<=n;++i)
	modify(root[i],root[i-1],1,n,a[i]);
	while(m--)
	{
    
    
		int x=read(),y=read();
		int ans=query(root[y],root[x-1],1,n,((y-x+1)>>1)+1);
		printf("%d\n",ans);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Brian_Pan_/article/details/106920404