Description
给一个长度为n的序列a。1≤a[i]≤n。
m组询问,每次询问一个区间[l,r],是否存在一个数在[l,r]中出现的次数大于(r-l+1)/2。如果存在,输出这个数,否则输出0。
Input
第一行两个数n,m。
第二行n个数,a[i]。
接下来m行,每行两个数l,r,表示询问[l,r]这个区间。
Output
m行,每行对应一个答案。
Sample Input
7 5
1 1 3 2 3 4 3
1 3
1 4
3 7
1 7
6 6
1 1 3 2 3 4 3
1 3
1 4
3 7
1 7
6 6
Sample Output
1
0
3
0
4
0
3
0
4
HINT
【数据范围】
n,m≤500000
第一次打可持久化线段树……
可持久化线段树是这样一个数据结构:它不仅实现线段树的操作,而且可以访问线段树的历史版本。
做法是“建n棵线段树”。
当然不是n^2logn的满二叉树啦
对于这题:首先搞出一颗权值线段树(就是区间(l,r)表示数字大小在(l,r)中的有多少个,a[i]<=10^9之类太大的要离散搞)。
每次增加一个数k进去的时候,相当于从根节点到叶节点的所有包含k的区间的sum+1。
但是我们要保留以前的版本,怎么做呢?
很容易发现每次加入一个k,修改的都只有树上的一条从根到叶的链上的sum,也就是说其他数据我们是可以共用的
所以每次加入k的时候只要再插入一条链就可以完成修改。
比如当前我们插入链做到一个点,首先把它的左右儿子改成它的上一个版本的左右儿子。然后看k在左边还是右边,再往那一边继续递归下去
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#define mx 10000010
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int n,m,treesize;
int root[500001];
int ls[mx],rs[mx],tot[mx];
inline void insert(int l,int r,int x,int &y,int dat)
{
y=++treesize;
tot[y]=tot[x]+1;
if (l==r) return;
ls[y]=ls[x];rs[y]=rs[x];
int mid=(l+r)>>1;
if (dat<=mid)insert(l,mid,ls[x],ls[y],dat);
else insert(mid+1,r,rs[x],rs[y],dat);
}
inline int ask(int l,int r)
{
int ll=1,rr=n,request=(r-l+1)>>1;
int x=root[l-1],y=root[r];
while (ll!=rr)
{
if (tot[y]-tot[x]<=request) return 0;
int mid=(ll+rr)>>1;
if (tot[ls[y]]-tot[ls[x]]>request)
{rr=mid;x=ls[x];y=ls[y];}
else if (tot[rs[y]]-tot[rs[x]]>request)
{ll=mid+1;x=rs[x];y=rs[y];}
else return 0;
}
return ll;
}
int main()
{
n=read();m=read();
for (int i=1;i<=n;i++)
{
int x=read();
insert(1,n,root[i-1],root[i],x);
}
for (int i=1;i<=m;i++)
{
int x=read(),y=read();
printf("%d\n",ask(x,y));
}
}