BZOJ 3524 [Poi2014]Couriers(可持久化线段树)
【题目链接】 http://www.lydsy.com/JudgeOnline/problem.php?id=3524
【题目大意】
给一个长度为n的序列a。1≤a[i]≤n。
m组询问,每次询问一个区间[l,r],是否存在一个数在[l,r]中出现的次数大于(r-l+1)/2。
如果存在,输出这个数,否则输出0。
【题解】
建立可持久化的权值线段树,对于区间查询,
在线段树上二分查询R和L-1版本间的数值差符合要求的位置。
【代码】
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int N=500010; int n,m,i,x,y,z,ans; int l[N*40],r[N*40],v[N*40],tot,root[N],a[N],pre[N]; void read(int&a){ char ch;while(!((ch=getchar())>='0')&&(ch<='9')); a=ch-'0';while(((ch=getchar())>='0')&&(ch<='9'))a*=10,a+=ch-'0'; } int build(int a,int b){ int x=++tot; v[x]=0; if(a==b)return x; int mid=(a+b)>>1; return l[x]=build(a,mid),r[x]=build(mid+1,b),x; } // x版本c位置+p,返回更新后版本根节点id int change(int x,int a,int b,int c,int p){ int y=++tot;v[y]=v[x]+p; if(a==b)return y; int mid=(a+b)>>1; if(c<=mid)l[y]=change(l[x],a,mid,c,p),r[y]=r[x]; else l[y]=l[x],r[y]=change(r[x],mid+1,b,c,p); return y; } // 查询[lx,rx]区间内超过cnt的数(cnt>=区间长1/2) int query(int lx,int rx,int cnt){ int L=1,R=n,mid,x,y; x=root[lx-1],y=root[rx]; while(L!=R){ if(v[y]-v[x]<=cnt)return 0; mid=(L+R)>>1; if(v[l[y]]-v[l[x]]>cnt)R=mid,x=l[x],y=l[y]; else if(v[r[y]]-v[r[x]]>cnt)L=mid+1,x=r[x],y=r[y]; else return 0; }return L; } int main(){ while(~scanf("%d%d",&n,&m)){ root[tot=0]=build(1,n); for(int i=1;i<=n;i++){ int x; read(x); root[i]=change(root[i-1],1,n,x,1); } for(int i=1;i<=m;i++){ int l,r; scanf("%d%d",&l,&r); printf("%d\n",query(l,r,(r-l+1)>>1)); } }return 0; }
愿你出走半生,归来仍是少年