[POI2014]Couriers
OJ题号:BZOJ3524、BZOJ2223、洛谷3567
思路:
维护一颗可持久化权值线段树,记录每次加入数字时,不同数字出现的个数。
对于每一个询问$[l,r]$,同时查询以$r$和$l-1$为根的线段树,每次比较两个节点左右字子树的权值和,如果大于$[l,r]$区间的一半就说明这一子区间可能有答案,递归查询即可。
1 #include<cstdio> 2 #include<cctype> 3 #include<cstring> 4 inline int getint() { 5 char ch; 6 while(!isdigit(ch=getchar())); 7 int x=ch^'0'; 8 while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0'); 9 return x; 10 } 11 const int N=500001,SZ=10000001; 12 class FotileTree { 13 private: 14 int val[SZ],sz,left[SZ],right[SZ]; 15 int newnode() { 16 return sz++; 17 } 18 public: 19 FotileTree() { 20 sz=0; 21 memset(val,0,sizeof val); 22 } 23 int root[N]; 24 int build(const int b,const int e) { 25 int new_p=newnode(); 26 if(b==e) return new_p; 27 int mid=(b+e)>>1; 28 left[new_p]=build(b,mid); 29 right[new_p]=build(mid+1,e); 30 return new_p; 31 } 32 int modify(const int p,const int b,const int e,const int x) { 33 int new_p=newnode(); 34 val[new_p]=val[p]+1; 35 if(b==e) return new_p; 36 int mid=(b+e)>>1; 37 if(x<=mid) left[new_p]=modify(left[p],b,mid,x),right[new_p]=right[p]; 38 if(x>mid) right[new_p]=modify(right[p],mid+1,e,x),left[new_p]=left[p]; 39 return new_p; 40 } 41 int query(const int p1,const int p2,const int b,const int e,const int k) { 42 if(b==e) return b; 43 int mid=(b+e)>>1; 44 if(val[left[p2]]-val[left[p1]]>k) return query(left[p1],left[p2],b,mid,k); 45 if(val[right[p2]]-val[right[p1]]>k) return query(right[p1],right[p2],mid+1,e,k); 46 return 0; 47 } 48 }; 49 FotileTree t; 50 int main() { 51 int n=getint(),m=getint(); 52 t.root[0]=t.build(1,n); 53 for(int i=1;i<=n;i++) t.root[i]=t.modify(t.root[i-1],1,n,getint()); 54 while(m--) { 55 int l=getint(),r=getint(); 56 printf("%d\n",t.query(t.root[l-1],t.root[r],1,n,(r-l+1)>>1)); 57 } 58 return 0; 59 }