BZOJ 3524 [POI2014] Couriers/洛谷3567(可持久化线段树)
题意:给你n个数,有m次询问,问你有没有[l,r]区间内有一个出现的次数大于等于区间长度的一半
思路:主席树的节点y代表1到y,然后减去左端点x就得到的是区间[x,y]的信息,这样我们判断权值线段树上的左侧还是右侧大于k次,如果存在的话我们就一项向下找,找到子节点,看子节点是不是大于等于k次,如果是就存在,不是就返回0
代码:(新离散化板子)
#include <set> #include <map> #include <queue> #include <stack> #include <math.h> #include <vector> #include <string> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <iostream> #include <algorithm> #define zero(a) fabs(a)<eps #define max( x, y ) ( ((x) > (y)) ? (x) : (y) ) #define min( x, y ) ( ((x) < (y)) ? (x) : (y) ) #define lowbit(x) (x&(-x)) #define debug(a) cerr<<#a<<"=="<<a<<endl typedef long long LL; const double pi=acos(-1.0); const double eps=1e-8; const int inf=0x3f3f3f3f; const LL linf=0x3f3f3f3f3f3f3f3f; using namespace std; const int maxn=500000+7; int n,m,a[maxn],root[maxn],cnt,x,y,k,sa[maxn],tot; struct node { int l,r,sum; }T[maxn*40]; vector<int>v; int getid(int x) { return lower_bound(sa+1,sa+tot+1,x)-sa; } void update(int l,int r,int &x,int y,int pos) { T[++cnt]=T[y],T[cnt].sum++,x=cnt; if(l==r)return ; int mid=(l+r)>>1; if(pos<=mid)update(l,mid,T[x].l,T[y].l,pos); else update(mid+1,r,T[x].r,T[y].r,pos); } int query(int l,int r,int x,int y,int k) { if(l==r)return l; int mid=(l+r)>>1; int lans=T[T[y].l].sum-T[T[x].l].sum,rans=T[T[y].r].sum-T[T[x].r].sum; if(lans>=k)query(l,mid,T[x].l,T[y].l,k); else if(rans>=k)query(mid+1,r,T[x].r,T[y].r,k); else return 0; } int main() { cnt=0,tot=0; scanf("%d%d",&n,&m); for(int i=1;i<=n;i++){ scanf("%d",&a[i]); sa[i]=a[i]; } sort(sa+1,sa+n+1); tot=unique(sa+1,sa+1+n)-sa-1; for(int i=1;i<=n;i++){ update(1,tot,root[i],root[i-1],getid(a[i])); } // printf("fuck %d\n",tot); while(m--){ int t1,t2; scanf("%d%d",&t1,&t2); int ans=query(1,tot,root[t1-1],root[t2],(t2-t1+1)/2+1); // printf("test %d\n",ans); printf("%d\n",sa[ans]); } return 0; }