1. 程式人生 > >[靜態主席樹]Couriers

[靜態主席樹]Couriers

題目描述

給一個長度為n的序列a。1≤a[i]≤n。
m組詢問,每次詢問一個區間[l,r],是否存在一個數在[l,r]中出現的次數大於(r-l+1)/2。如果存在,輸出這個數,否則輸出0。

 

輸入

第一行兩個數n,m(n,m≤500000)。
第二行n個數,a[i]。
接下來m行,每行兩個數l,r,表示詢問[l,r]這個區間。

 

輸出

m行,每行對應一個答案。

 

樣例輸入

7 5
1 1 3 2 3 4 3
1 3
1 4
3 7
1 7
6 6

樣例輸出

1
0
3
0
4

思路:建立可持久化權值線段樹
AC程式碼:
#include <iostream>
#include<cstdio>
using namespace std;

int x,n,m;
int ls[10000010],rs[10000010],num[10000010],tot=0;
int tree[500010];

inline void update(int pre,int& cur,int l,int r,int v){
    cur=++tot;
    num[cur]=num[pre]+1;
    if(l==r) return;
    ls[cur]=ls[pre],rs[cur]=rs[pre];
    
int m=(l+r)>>1; if(v<=m) update(ls[pre],ls[cur],l,m,v); else update(rs[pre],rs[cur],m+1,r,v); } int ask(int ltree,int rtree,int k){ int l=1,r=n; while(l<r){ int m=(l+r)>>1; if(num[ls[rtree]]-num[ls[ltree]]>=k){ ltree=ls[ltree],rtree=ls[rtree]; r
=m; } else if(num[rs[rtree]]-num[rs[ltree]]>=k){//區間內只可能有1個數出現次數超過一半 ltree=rs[ltree],rtree=rs[rtree]; l=m+1; } else return 0; } return l; } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++){ scanf("%d",&x); update(tree[i-1],tree[i],1,n,x); } while(m--){ int l,r;scanf("%d%d",&l,&r); int ans=ask(tree[l-1],tree[r],(r-l+1)/2+1); printf("%d\n",ans); } return 0; }