【主席树】BZOJ3524-[Poi2014]Couriers
【题目大意】
给一个长度为n的序列a。1≤a[i]≤n。
m组询问,每次询问一个区间[l,r],是否存在一个数在[l,r]中出现的次数大于(r-l+1)/2。如果存在,输出这个数,否则输出0。
【思路】
只有query部分需要稍作修改。由于每个节点代表的是一个大小区间数的总数,所以只需判断左右子数的sum值是否大于(r-l+1)/2,满足继续往左右子树查询。没有满足条件的就返回0。由于当前的节点的sum值一定是大于(r-l+1)/2的,如果l==r直接返回l即可。
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<vector> 6 #define lson l,m 7 #define rson m+1,r 8 using namespace std; 9 const int MAXN=500000+50; 10 int n,m,d,a[MAXN],hash[MAXN],tot; 11 int T[MAXN],L[MAXN<<5],R[MAXN<<5],sum[MAXN<<5]; 12 13 int build(int l,int r) 14 { 15 int rt=++tot; 16 sum[rt]=0; 17 if (l!=r) 18 { 19 int m=(l+r)>>1; 20 L[rt]=build(lson); 21 R[rt]=build(rson); 22 } 23 return rt; 24 } 25 26 int update(int pre,int l,int r,int x) 27 { 28 int rt=++tot; 29 L[rt]=L[pre],R[rt]=R[pre],sum[rt]=sum[pre]+1; 30 if (l!=r) 31 { 32 int m=(l+r)>>1; 33 if (x<=m) L[rt]=update(L[pre],lson,x); 34 else R[rt]=update(R[pre],rson,x); 35 } 36 return rt; 37 } 38 39 int query(int lrt,int rrt,int l,int r,int k) 40 { 41 if (l==r) return l; 42 int m=(l+r)>>1; 43 if (sum[L[rrt]]-sum[L[lrt]]>k) return query(L[lrt],L[rrt],lson,k); 44 if (sum[R[rrt]]-sum[R[lrt]]>k) return query(R[lrt],R[rrt],rson,k); 45 return 0; 46 } 47 48 void init() 49 { 50 scanf("%d%d",&n,&m); 51 for (int i=1;i<=n;i++) scanf("%d",&a[i]),hash[i]=a[i]; 52 sort(hash+1,hash+n+1); 53 d=unique(hash+1,hash+n+1)-(hash+1); 54 55 tot=0; 56 T[0]=build(1,d); 57 for (int i=1;i<=n;i++) 58 { 59 int x=lower_bound(hash+1,hash+d+1,a[i])-hash; 60 //注意离散化之后是从1开始,所以减去的是hash而不是hash+1 61 T[i]=update(T[i-1],1,d,x); 62 } 63 } 64 65 void solve() 66 { 67 for (int i=0;i<m;i++) 68 { 69 int l,r; 70 scanf("%d%d",&l,&r); 71 int ans=query(T[l-1],T[r],1,d,(r-l+1)>>1); 72 printf("%d\n",hash[ans]); 73 } 74 } 75 76 int main() 77 { 78 init(); 79 solve(); 80 return 0; 81 }