[BZOJ 3524] kur 绝对众数

题意

  给定长度为 n 的序列 A = {a[1], a[2], ..., a[n]} .

  m 次询问, 求区间 [l, r] 的绝对众数.

  n, m <= 100000 .

分析

  性质1. 如果存在绝对众数, 那么中位数一定是绝对众数.

  性质2. 不同数两两匹配, 最后剩下来的是绝对众数.

  建立可持久化线段树, 根据 性质1 , 找中位数有多少个.

  我们用 性质2 带来的摩尔投票法进行对拍.

实现

  一般来说, 如果要找中位数, 都用 n+1 >> 1 , 而不是 n >> 1 , 否则当 n = 1 的时候就gg了.

  

  AC程序:

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <cstdlib>
 4 #include <cctype>
 5 #define F(i, a, b) for (register int i = (a); i <= (b); i++)
 6 inline int rd(void) {
 7     int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == ‘-‘) f = -1;
 8     int x = 0; for (; isdigit(c); c = getchar()) x = x*10+c-‘0‘; return x*f;
 9 }
10
11 const int N = 500005;
12 const int S = 10000000;
13
14 int n, m, a[N];
15 int rt[N], tot, Lc[S], Rc[S], cnt[S];
16
17 #define M ((L+R)>>1)
18 inline int Insert(int x, int L, int R, int pos) {
19     int cur = ++tot; Lc[cur] = Lc[x], Rc[cur] = Rc[x], cnt[cur] = cnt[x] + 1;
20     if (L != R)
21         pos <= M ? Lc[cur] = Insert(Lc[x], L, M, pos) : Rc[cur] = Insert(Rc[x], M+1, R, pos);
22     return cur;
23 }
24 inline int Query(int x, int y, int L, int R, int K, int Siz) {
25     if (L == R) return cnt[y] - cnt[x] > Siz ? L : 0;
26     int Lsz = cnt[Lc[y]] - cnt[Lc[x]];
27     return K <= Lsz ? Query(Lc[x], Lc[y], L, M, K, Siz) : Query(Rc[x], Rc[y], M+1, R, K - Lsz, Siz);
28 }
29
30 int main(void) {
31     #ifndef ONLINE_JUDGE
32         freopen("kur.in", "r", stdin);
33     #endif
34
35     n = rd(), m = rd();
36     F(i, 1, n) a[i] = rd();
37     F(i, 1, n) rt[i] = Insert(rt[i-1], 1, n, a[i]);
38
39     F(i, 1, m) {
40         int l = rd(), r = rd();
41         printf("%d\n", Query(rt[l-1], rt[r], 1, n, (r-l+2)/2, (r-l+1)/2));
42     }
43
44     return 0;
45 }

  对拍程序:

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <cstdlib>
 4 #include <cctype>
 5 #define F(i, a, b) for (register int i = (a); i <= (b); i++)
 6 inline int rd(void) {
 7     int f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == ‘-‘) f = -1;
 8     int x = 0; for (; isdigit(c); c = getchar()) x = x*10+c-‘0‘; return x*f;
 9 }
10
11 const int N = 500005;
12
13 int n, m, a[N];
14
15 int Query(int l, int r) {
16     int key = 0, cnt = 0;
17     F(i, l, r) {
18         if (!cnt) key = a[i];
19         if (key == a[i]) cnt++; else cnt--;
20     }
21     cnt = 0;
22     F(i, l, r)
23         cnt += (key == a[i]);
24     return cnt > (r-l+1)/2 ? key : 0;
25 }
26
27 int main(void) {
28     #ifndef ONLINE_JUDGE
29         freopen("kur.in", "r", stdin);
30     #endif
31
32     n = rd(), m = rd();
33     F(i, 1, n) a[i] = rd();
34
35     F(i, 1, m) {
36         int l = rd(), r = rd();
37         printf("%d\n", Query(l, r));
38     }
39
40     return 0;
41 }
时间: 2024-10-29 04:37:30

[BZOJ 3524] kur 绝对众数的相关文章

BZOJ 3524: [Poi2014]Couriers

3524: [Poi2014]Couriers Time Limit: 20 Sec  Memory Limit: 256 MBSubmit: 1905  Solved: 691[Submit][Status][Discuss] Description 给一个长度为n的序列a.1≤a[i]≤n.m组询问,每次询问一个区间[l,r],是否存在一个数在[l,r]中出现的次数大于(r-l+1)/2.如果存在,输出这个数,否则输出0. Input 第一行两个数n,m.第二行n个数,a[i].接下来m行,

BZOJ 3524: [Poi2014]Couriers( 主席树 )

卡我空间.... 这道题应该是主席树入门题...无修改 , 离散化都不用...出题人业界良心啊 一开始的空白树我 build 出来结果就多了整整 2n 个 Node , 然后就 MLE 了... ( 双倍经验 , 另一道见上图 ) ---------------------------------------------------------------------------------------------- #include<cstdio> #include<cstring&g

BZOJ 3524 [Poi2014]Couriers(二分+蒙特卡罗)

[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=3524 [题目大意] 给一个长度为n的序列a.1≤a[i]≤n. m组询问,每次询问一个区间[l,r],是否存在一个数在[l,r]中出现的次数大于(r-l+1)/2. 如果存在,输出这个数,否则输出0. [题解] 我们可以在[l,r]中随机一个位置检验这个位置上数是不是答案, 检测方法可以在数组中保存每个数在序列中的不同位置,二分即可 选中答案的概率为1/2,我们做k次蒙特卡罗,正确率

Couriers(bzoj 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行,每行对应一个答案. Sample Input 7 5 1 1 3 2 3 4 3 1 3 1 4 3 7 1 7 6 6 Sample Output 1 0 3

BZOJ 3524 [Poi2014]Couriers 可持久化线段树

题意:链接 方法:可持久化线段树. 解析: 可持久化数据结构好神啊,感觉都好玄妙的感觉. 首先建树的目的就是建立一棵权值树,维护的是在L,R里某些权值的数的出现个数.然后呢,对于1~n每个节点建一棵树,并且是基于前一棵树的基础上的.然后对于每一次的新值我们只需要update一次,并且连接一下原来的树? 神犇们不是说这种结构就是一堆线段树连啊连就出来了吗. 查询的时候呢?有一些小改变,据说是以二分为基础的查询. 神犇们发明这种数据结构的时候,就发现了这种数据结构里的所有线段树是可以相减的这种性质,

BZOJ 3524 POI 2014 Couriers 主席树

题目大意 给出一个序列,问一段区间内有没有出现过一半以上的数字. 思路 用主席树取区间出来,在权值线段树上找. CODE #define _CRT_SECURE_NO_WARNINGS #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define MAX 500010 #define MAXR 10000010 using namespace std

【bzoj 3524】[Poi2014]Couriers

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行,每行对应一个答案. Sample Input 7 5 1 1 3 2 3 4 3 1 3 1 4 3 7 1 7 6 6 Sample Output 1 0 3

bzoj 3524 [POI2014]KUR-Couriers (主席树)

题目大意:给你一个序列,求某个区间出现次数大于一半的数是什么 主席树裸题,刷刷水题提升自信= = 1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #define ll long long 5 #define il inline 6 #define N 500100 7 using namespace std; 8 //re 9 int n,m,tot; 10 int a[N],root[

【BZOJ】【3524】【POI2014】Couriers

可持久化线段树 裸可持久化线段树,把区间第K大的rank改成num即可……(往儿子走的时候不减少) 苦逼的我……MLE了一次(N*30),RE了一次(N*10)……数组大小不会开…… 最后开成N*20的过了 1 /************************************************************** 2 Problem: 3524 3 User: Tunix 4 Language: C++ 5 Result: Accepted 6 Time:5752 ms