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)); } }
——by zhber,转载请注明来源