Main element line tree

Main element

Title description

Given an array A[1...n], and then M queries, the format of the i-th query is: Li, Ri, and the meaning is: Does it exist in the interval from A[Li] to A[Ri] "Main element", if it exists, output "yes" and the main element, otherwise output "no".
The following explains the "primary element": In an interval containing X elements, if an element appears strictly greater than X/2, then that element is the principal element of the interval.

Input format

The first line, n and C. 3 <= n <= 300000, 1 <= C <= 10000.

In the second line, n positive integers, the i-th positive integer represents A[i]. Where 1<=A[i]<=C.

In the third line, an integer M. 1 <= M <= 10000.

There are M rows next, and the i-th row gives: Li and Ri. 1 <= Li <= Ri <= n.

Output format

There are a total of M lines, and each line corresponds to a query.

Input sample

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

Sample output

no
yes 1
no
yes 1
no
yes 2
no
yes 3


Problem solution boundary



answer

This question involves the interval problem, first consider using the data structure of the line segment tree

However, this question does not involve classical line segment trees such as interval sum and maximum interval, but involves the number of elements.

Then can we transform the line segment tree to adapt to this problem? of course can

First consider the characteristics of the main element:

1. In an interval containing X elements, the number of occurrences of an element is strictly greater than X/2

2. There may be no principal element in this interval

After careful analysis, we can find:

There are two intervals [l1,r1] and [l2,r2],(l1<=r1<=l2<=r2), then the main elements of these two intervals have the following relationship:

1. If both [l1,r1] and [l2,r2] have no principal elements, then the interval [l1,r2] also has no principal elements . Prove:

	∵[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. If both [l1,r1] and [l2,r2] have the same principal element x, then the principal element of the interval [l1,r2] is x . Prove:

	∵[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. If [l1,r1] and [l2,r2] have principal elements and are x and y respectively (x≠y), then the interval [l1,r2] may have no principal element, and it may be that the principal element is x, It is possible that the main element is y

At this time, we have summarized the method of maintaining the line segment tree, but there is still no definite idea for the specific realization of the third part.

We might as well try to enumerate the interval [l1, r2], find the number of x and the number of y, and determine the main element by comparison

At this time, we also found that if it is directly exposed , but the number of queries is large and r2-l1 is relatively large, then it must be TLE (Time Limit Exceed). Reduce the time limit of the exposure! [] Complexity, we are very It is easy to think that only two points can be used to reduce O(n) to O(log2n).

The question is: how to divide

There is a clever way to help us:

First open a pos array, we use the pos array to store all positions in each element sequence

Open a head array to record the starting position of each element in pos

For convenience, open the sum array and record the number of each element

Insert picture description here

Then we can easily find the value of tail, in the range of head and tail, the binary enumeration is in [l1,r2]

The position where the leftmost x appears is at position pos1 in pos

And the position of the rightmost x in position pos2 in pos ,

The number of x is num=pos2-pos1

In this way, this question is completed

#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;
}

Guess you like

Origin blog.csdn.net/bell041030/article/details/88765687