主元素
题目描述
给出一个数组A[1…n],再给出M个询问,第i个询问的格式是:Li、Ri,表示的意义是:A[Li]至A[Ri]这个区间中,是否存在“主元素”,如果存在则输出“yes”和主元素,否则输出“no”。
下面解释“主元素”:在一个包含X个元素的区间里面,如果有个元素出现的次数严格大于X/2,那么该元素就是区间的主元素。
输入格式
第一行,n和C。3 <= n <= 300000,1 <= C <= 10000。
第二行,n个正整数,第i个正整数表示A[i]。 其中1<=A[i]<=C。
第三行,一个整数M。1 <= M <= 10000。
接下来有M行,第i行给出:Li和Ri。1 <= Li <= Ri <= n。
输出格式
共M行,每行对应一个询问。
输入样例
10 3
1 2 1 2 1 2 3 2 3 3
8
1 2
1 3
1 4
1 5
2 5
2 6
6 9
7 10
输出样例
no
yes 1
no
yes 1
no
yes 2
no
yes 3
题目题解分界线
题解
本题涉及到了区间问题,首先考虑使用线段树这个数据结构
但是本题并没有涉及区间和、区间最大等经典线段树的问题,而是涉及元素的数量问题
那我们可不可以改造线段树来适应这一道题呢?当然可以
首先来思考一下主元素的特点:
1、在一个包含X个元素的区间里面,有一个元素出现的次数严格大于X/2
2、这个区间里可能没有主元素
再仔细分析,可以发现:
设有两个区间[l1,r1]和[l2,r2],(l1<=r1<=l2<=r2),那么这两个区间的主元素有以下关系:
1、若[l1,r1]和[l2,r2]都没有主元素,那么区间[l1,r2]也没有主元素,证明:
∵[l1,r1]和[l2,r2]都没有主元素
∴设出现次数最多的数x分别在两个区间内出现了(r1-l1)/2次和(r2-l2)/2次
∴x在[l1,r2]内出现了(r2-l2+r1-l1)/2次,即(r2-l1)/2次
∵(r2-l1)/2=(r2-l1)/2
∴若[l1,r1]和[l2,r2]都没有主元素,那么区间[l1,r2]也没有主元素
2、若[l1,r1]和[l2,r2]都有相同的主元素x,那么区间[l1,r2]的主元素为x,证明:
∵[l1,r1]和[l2,r2]都有主元素x
∴x分别在两个区间内最少出现了(r1-l1)/2+1次和(r2-l2)/2+1次
∴x在[l1,r2]内出现了(r2-l2+r1-l1)/2+2次,即(r2-l1)/2+2次
∵(r2-l1)/2+2>(r2-l1)/2
∴若[l1,r1]和[l2,r2]有相同的主元素x,那么区间[l1,r2]的主元素为x
3、若[l1,r1]和[l2,r2]都有主元素且分别为x和y(x≠y),那么区间[l1,r2]有可能没有主元素,有可能主元素为x,有可能主元素为y
此时我们就归纳出了维护线段树的做法,但是对于第三部的具体实现,仍然还没有一个确切的想法,
我们不妨尝试枚举区间[l1,r2],寻找x的个数和y的个数,通过比较确定主元素
这时我们又发现,如果直接暴枚,但询问次数多且r2-l1比较大时,那是肯定要TLE(Time Limit Exceed 超时)的,降低暴枚的时间复![]杂度,我们很容易想到,用二分才能将O(n)降到O(log2n)。
问题就在于:怎么二分
有一种巧妙的做法可以帮助我们:
首先开一个pos数组,我们用pos数组储存每一种元素序列中的所有位置
再开一个head数组,记录每一种元素在pos中开始的位置
为了方便,开sum数组,记录每一种元素的数量
然后我们就能轻易求出tail的值,在head和tail的范围内二分枚举在[l1,r2]内
最左边x出现的位置在pos中的位置pos1
和最右边x的位置在pos中的位置pos2,
x的数量为num=pos2-pos1
这么一来,这道题就完成了
#include<iostream>
using namespace std;
int n,c,m,a[300005],b[10005],head[10005],pos[300005],x,y;
struct node{
int mainnum;
int sum;
}tree[1200020],nul/*表示空*/,ans;
int searchmain(int le,int ri,int x)//原理:二分查找
{
if(x==0)
return 0;
/*确定元素x在位置查询表中 最开始出现的位置 与 最后出现的位置*/
int start=head[x]-b[x]+1;
int end=head[x];
/*----------------------------------------------------------*/
int middle=0,low=0,high=0;
if(pos[end]<le||pos[start]>ri)//不在区间内的情况
return 0;
if(pos[start]>=le&&pos[end]<=ri)//被区间包含的情况
return end-start+1;
/*二分查找左端点*/
while(start<=end)
{
middle=(start+end)/2;
if(pos[middle]<le)
start=middle+1;
else
{
end=middle-1;
low=middle;
}
}
start=head[x]-b[x]+1,end=head[x];
/*二分查找右端点*/
while(start<=end)
{
middle=(start+end)/2;
if(pos[middle]<=ri)
{
start=middle+1;
high=middle;
}
else
end=middle-1;
}
return high-low+1;
}
void Toper(node &rt,int lmain,int rmain,int left,int right)//维护操作
{
if(lmain==rmain)//如果两边的主元素相同,则两边的主元素为本区间的主元素
{
rt.mainnum=lmain;
rt.sum=searchmain(left,right,lmain);
}
else
{
int temp1=searchmain(left,right,lmain);//找左树的主元素
int temp2=searchmain(left,right,rmain);//找右树的主元素
/*--------确定主元素--------*/
if(temp1>(right-left+1)/2)
{
rt.mainnum=lmain;
rt.sum=temp1;
}
if(temp2>(right-left+1)/2)
{
rt.mainnum=rmain;
rt.sum=temp2;
}
/*--------------------------*/
}
}
void build(int k,int l,int r)
{
if(l==r)
{
tree[k].mainnum=a[l];
tree[k].sum=1;
return;
}
int mid=(l+r)/2;
build(k*2,l,mid);
build(k*2+1,mid+1,r);
Toper(tree[k],tree[k*2].mainnum,tree[k*2+1].mainnum,l,r);
}
node qaf(int k,int l,int r,int x,int y)
{
if(l>y||r<x) return nul;//如果目前区间不在需求区间内,返回空值
/*-------如果在区间内,直接返回主元素-------*/
if(l>=x&&r<=y)
return tree[k];
/*-----------------------------------------*/
int mid=(l+r)/2;
node lc,rc,answ;
lc.mainnum=lc.sum=rc.mainnum=rc.sum=answ.mainnum=answ.sum=0;
lc = qaf(k*2,l,mid,x,y);//在左子树找主元素
rc = qaf(k*2+1,mid+1,r,x,y);//在右子树找主元素
/*--有其中一个子树不在需求区间时,另一个则可作为需求区间的主元素--*/
if(lc.mainnum==-1)
return rc;
if(rc.mainnum==-1)
return lc;
if(lc.mainnum==rc.mainnum)
return lc;
/*--------------------------------------------------------------*/
/*----------确定需求区间的主元素----------*/
Toper(answ,lc.mainnum,rc.mainnum,x,y);
/*----------------------------------------*/
return answ;
}
int main()
{
nul.mainnum=-1;
nul.sum=-1;
cin>>n>>c;
for(int i=1;i<=n;i++)
{
cin>>a[i];
b[a[i]]++;
}
//-------位置查询表-------
for(int i=2;i<=c;i++)
head[i]=head[i-1]+b[i-1];
for(int i=1;i<=n;i++)
{
head[a[i]]++;
pos[head[a[i]]]=i;
}
//-------------------------
build(1,1,n);
cin>>m;
for(int i=1;i<=m;i++)
{
cin>>x>>y;
ans=qaf(1,1,n,x,y);
if(ans.mainnum)
cout<<"yes "<<ans.mainnum<<endl;
else
cout<<"no"<<endl;
}
return 0;
}