luogu P3567 [POI2014] Couriers

传送门

给一个数列,每次询问一个区间内有没有一个数出现次数超过一半

区间的一半就是(r-l+1)/2

对于主席树中的两棵树L,R中的同一个节点[l,r] 如果sum的差值大于k

说明在原序列下标L`R的区间中出现了多于k个权值处于[l,r]的数字

所以按照这个套路二分就行

然后这题bzoj有双倍经验 这里粘的这道题要离散就这个了

Code:

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cstring>
 4 #include<vector>
 5 #include<algorithm>
 6 #define rep(i,a,n) for(int i = a;i <= n;i++)
 7 #define per(i,n,a) for(int i = n;i >= a;i--)
 8 using namespace std;
 9 typedef long long ll;
10 int read() {
11     int as = 0,fu = 1;
12     char c = getchar();
13     while(c < '0' || c > '9') {
14         if(c == '-') fu = -1;
15         c = getchar();
16     }
17     while(c >= '0' && c <= '9') {
18         as = as * 10 + c - '0';
19         c = getchar();
20     }
21     return as * fu;
22 }
23 //head
24 const int N = 500005;
25 const int M = 10000005;
26 #define int ll
27 int n,Q,T,tot;
28 int a[N],b[N];
29 int rt[N],ls[M],rs[M],sum[M];
30 void ins(int l,int r,int &t,int pre,int q) {
31     t = ++tot,ls[t] = ls[pre],rs[t] = rs[pre];
32     sum[t] = sum[pre] + 1;
33     if(l == r) return;
34     int m = l+r >> 1;
35     if(q <= m) ins(l,m,ls[t],ls[pre],q);
36     else ins(m+1,r,rs[t],rs[pre],q);
37 }
38 
39 int query(int l,int r,int t,int pre,int k) {
40     if(l == r) return l;
41     int m = l+r >> 1;
42     int tmp1 = sum[ls[t]] - sum[ls[pre]];
43     int tmp2 = sum[rs[t]] - sum[rs[pre]];
44     if(k < tmp1) return query(l,m,ls[t],ls[pre],k);
45     if(k < tmp2) return query(m+1,r,rs[t],rs[pre],k);
46     return 0;
47 }
48 #undef int
49 
50 int main() {
51     n = read(),Q = read();
52     rep(i,1,n) a[i] = b[i] = read();
53     sort(b+1,b+n+1),T = unique(b+1,b+n+1) - (b+1);
54     rep(i,1,n) ins(1,T,rt[i],rt[i-1],lower_bound(b+1,b+T+1,a[i]) - b);
55     rep(i,1,Q) {
56          ll l = read(),r = read();
57         printf("%lld\n",b[query(1,T,rt[r],rt[l-1],r-l+1 >> 1)]);
58     }
59     return 0;
60 }

 

posted @ 2018-12-11 13:22  白怀潇  阅读(118)  评论(0编辑  收藏  举报