zoukankan      html  css  js  c++  java
  • 【ybt金牌导航6-3-1】【luogu P4168】区间众数 / 蒲公英 / 分块例题

    区间众数 / 蒲公英

    题目链接:ybt金牌导航6-3-1 / luogu P4168

    题目大意

    给出一个序列,每次求一个区间的众数。
    众数:出现次数最多的数,如果有多个数字,则选小的那个。
    强制在线。

    思路

    这道题是分块的例题。

    分块就是把数列分成很多个块,然后这些块是可以整个算的。
    这样的话,如果每次你要询问区间,你可以把它分成三个部分:前面多出来的,后面多出来的,中间若干个区间。
    前面后面多出来的我们可以直接暴力,然后中间就可以弄个前缀和或者什么的快速地求出来。

    然后我们想想分块,每个块的大小是多少最优。
    你既要顾忌块的个数,还要顾忌块的大小,那我们就让这两个尽可能相同,那就是 (sqrt{n}) 的大小,分成大概 (sqrt{n}) 段。
    (是大概是因为最后可以会多出来一点,但是这一点无所谓,算就是了)

    那这道题也是这样子,但是你看到数字很大,然后决定要记录每个数字在区间中出现的次数。
    那就把数字离散化一下,然后就按着上面的方法做。

    然后具体可以看一下代码。

    代码

    #include<cmath>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    
    using namespace std;
    
    struct node {
    	int x, num, val;
    }a[40001];
    int n, m, dy[40001], tot, K;
    int num[201][40001], x, y, ans;
    int sum[40001], maxn, maxx;
    
    bool cmp(node x, node y) {
    	return x.x < y.x;
    }
    
    bool cmp_b(node x, node y) {
    	return x.num < y.num;
    }
    
    int work(int x, int y) {
    	maxn = -1;
    	memset(sum, 0, sizeof(sum));
    	
    	while (x % K != 0 && x <= y) {//处理前面的多出来的
    		sum[a[x].val]++;
    		if (sum[a[x].val] > maxn) {
    			maxn = sum[a[x].val];
    			maxx = a[x].val;
    		}
    		else if (sum[a[x].val] == maxn && a[x].val < maxx) {
    			maxx = a[x].val;
    		}
    		x++;
    	}
    	while (y % K != K - 1 && x <= y) {//处理后面多出来的
    		sum[a[y].val]++;
    		if (sum[a[y].val] > maxn) {
    			maxn = sum[a[y].val];
    			maxx = a[y].val;
    		}
    		else if (sum[a[y].val] == maxn && a[y].val < maxx) {
    			maxx = a[y].val;
    		}
    		y--;
    	}
    	
    	if (x > y) {//没有中间的大块
    		return dy[maxx];
    	}
    	
    	for (register int i = 0; i <= tot; i++) {//枚举数
    		sum[i] += num[y / K][i] - ((x / K - 1 < 0) ? 0 : num[x / K - 1][i]);
    		//利用前缀和算出大区间中有多少个这个数
    		if (sum[i] > maxn) {
    			maxn = sum[i];
    			maxx = i;
    		}
    		else if (sum[i] == maxn && i < maxx) {
    			maxx = i;
    		}
    	}
    	
    	return dy[maxx];
    }
    
    int read() {
    	int re = 0;
    	char c = getchar();
    	while (c < '0' || c > '9') c = getchar();
    	while (c >= '0' && c <= '9') {
    		re = re * 10 + c - '0';
    		c = getchar();
    	}
    	return re;
    }
    
    void write(int now) {
    	if (now > 9) write(now / 10);
    	putchar(now % 10 + '0');
    }
    
    int main() {
    	n = read();
    	m = read();
    	for (register int i = 0; i < n; i++) {
    		a[i].x = read();
    		a[i].num = i;
    	}
    	
    	sort(a, a + n, cmp);//离散化
    	a[0].val = 0;
    	dy[tot] = a[0].x;
    	for (register int i = 1; i < n; i++) {
    		if (a[i].x != a[i - 1].x) {
    			tot++;
    			dy[tot] = a[i].x;
    		}
    		a[i].val = tot;
    	}
    	sort(a, a + n, cmp_b);
    	
    	K = floor(sqrt(n));//分块
    	for (register int i = 0; i < n; i++) {//算每个块每个数字的数量
    		num[i / K][a[i].val]++;
    	}
    	for (register int i = 1; i < n / K; i++)//前缀和算1~i个块中数字j的数量有多少个
    		for (register int j = 0; j <= tot; j++)
    			num[i][j] += num[i - 1][j];
    	
    	for (register int i = 1; i <= m; i++) {
    		x = read();
    		y = read();
    		x = ((x + ans - 1) % n) + 1;
    		y = ((y + ans - 1) % n) + 1;
    		if (x > y) swap(x, y);//解码
    		
    		x--;//因为我是从0编号,所以编号要减一
    		y--;
    		
    		ans = work(x, y);
    		
    		write(ans);
    		putchar('
    ');
    	}
    	
    	return 0;
    }
    
  • 相关阅读:
    c# 改变FileUpload 上传文件大小
    使用ActiveX读取客户端mac地址
    javascript小技巧
    【2012百度之星/资格赛】H:用户请求中的品牌 [后缀数组]
    POJ1012 约瑟夫环问题[双向循环链表+打表技巧]
    北大ACM题分类
    ACM大量习题题库
    POJ1423 计算出n的阶乘的位数大数问题[Stirling公式]
    ACM训练计划(下)
    POJ2080 角度问题[cmath函数]
  • 原文地址:https://www.cnblogs.com/Sakura-TJH/p/YBT_JPDH_6-3-1.html
Copyright © 2011-2022 走看看