BZOJ3524 [POI2014] Couriers
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3524
Description
给一个长度为n的序列a。1≤a[i]≤n。
m组询问,每次询问一个区间[l,r],是否存在一个数在[l,r]中出现的次数大于(r-l+1)/2。如果存在,输出这个数,否则输出0。
Input
第一行两个数n,m。
第二行n个数,a[i]。
接下来m行,每行两个数l,r,表示询问[l,r]这个区间。
Output
m行,每行对应一个答案。
主席树 OrzTLE Orz林教主 建的是权值线段树。
最开始一直不懂为什么可以在权值线段树上二分一个数来判断它是否符合条件,后来明白了
如果存在一个数出现次数大于(r-l+1)/2,它的出现次数不仅是最大,而且比其他的数加起来的都要大
那么它所在的左边那一半或右边那一半的出现次数总和肯定大于另一半的总和
所以二分后如果存在就一定能找到那个数
注意数组大小,这题卡空间……
做了一下询问过程加inline,不加inline和直接塞进主函数的效率对比
从上到下依次是直接塞进主函数,加inline,不加inline
加与不加之间相差大约300ms,直接塞进主函数和加inline运行时间没有太大区别
1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 #include <cstring> 5 #define rep(i,l,r) for(int i=l; i<=r; i++) 6 #define clr(x,y) memset(x,y,sizeof(x)) 7 #define travel(x) for(Edge *p=last[x]; p; p=p->pre) 8 using namespace std; 9 const int INF = 0x3f3f3f3f; 10 const int maxn = 500010; 11 int n,m,x,y,tot=0,a[maxn],root[maxn],ls[maxn*20],rs[maxn*20],sum[maxn*20]; 12 inline int read(){ 13 int ans = 0, f = 1; 14 char c = getchar(); 15 while (!isdigit(c)){ 16 if (c == '-') f = -1; 17 c = getchar(); 18 } 19 while (isdigit(c)){ 20 ans = ans * 10 + c - '0'; 21 c = getchar(); 22 } 23 return ans * f; 24 } 25 void insert(int l,int r,int x,int &y,int v){ 26 y = ++tot; 27 sum[y] = sum[x] + 1; 28 if (l == r) return; 29 ls[y] = ls[x]; rs[y] = rs[x]; 30 int mid = (l + r) >> 1; 31 if (v <= mid) insert(l,mid,ls[x],ls[y],v); 32 else insert(mid+1,r,rs[x],rs[y],v); 33 } 34 inline void query(int x,int y){ 35 int l=1,r=n,a=root[y],b=root[x-1]; 36 while (l < r){ 37 int mid = (l + r) >> 1; 38 if (sum[ls[a]] - sum[ls[b]] > sum[rs[a]] - sum[rs[b]]){ 39 a = ls[a]; b = ls[b]; r = mid; 40 } 41 else{ 42 a = rs[a]; b = rs[b]; l = mid + 1; 43 } 44 } 45 if (sum[a] - sum[b] > (y - x + 1) >> 1) printf("%d\n",l); 46 else printf("0\n"); 47 } 48 int main(){ 49 n = read(); m = read(); 50 rep(i,1,n) a[i] = read(); 51 rep(i,1,n) insert(1,n,root[i-1],root[i],a[i]); 52 rep(i,1,m){ 53 x = read(); y = read(); 54 query(x,y); 55 } 56 return 0; 57 }