249. 蒲公英

题目链接:249. 蒲公英

算法分析

进阶指南上算法描述已经很清晰了。这里是用vector维护的每个数在序列中出现的下标。假设总共T块,预处理出每两个块之间的最小众数,需要时间O(nT)。m个查询小块内每个数的出现次数为O(mn/T*logn),总体时间复杂度为两者之和。

根据均值不等式,如果时间复杂度最低,应满足 n T = m n / T l o g n nT=mn/Tlogn nT=mn/Tlogn,求得 T = n l o g n T=\sqrt {nlogn} T=nlogn ,这里面n<=40000,用 b l o blo blo表示块的长度, b l o = n / T blo=n/T blo=n/T,求得 b l o blo blo约等于52。

因此: b l o = 52 blo = 52 blo=52

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <vector>
#define ll long long
#define inf 0x7fffffff 
using namespace std;
const int N = 40010;
int n, m, a[N], lsh[N], bl[N], num[N], ori[N];  // ori[]表示离散化后原来的数  
int f[1000][1000]; // 保存块与块之间的最小众数  
int blo = 52; // 根据均值不等式计算出来的块的大小  
vector<int> v[N];
int szread()
{
	int x = 0, f = 1; char c = getchar();
	while (c < '0' || c > '9') {if (c == '-') f = -1; c = getchar();}
	while (c >= '0' && c <= '9') {x = x * 10 + c - '0'; c = getchar();}
	return x * f; 
}
void cal(int t)  // 第t块  
{
	int val = 0, maxn = -1;
	memset(num, 0, sizeof(num));
	for (int i = (t - 1) * blo + 1; i <= n; ++i)
	{
		++num[a[i]];
		if (num[a[i]] > maxn || (num[a[i]] == maxn && a[i] < val))
		{
			maxn = num[a[i]];
			val = a[i]; 
		}		
		f[t][bl[i]] = val;  // 放到if外面  
	}
}
void pre()
{
	for (int i = 1; i <= n; ++i) v[a[i]].push_back(i);
	for (int i = 1; i <= bl[n]; ++i) cal(i); // 计算[i, bl[n]]块的f值  
}
int main()
{
	n = szread(); m = szread();
	for (int i = 1; i <= n; ++i) a[i] = lsh[i] = szread();
	// 离散化  
	sort(lsh + 1, lsh + n + 1);
	int cnt = unique(lsh + 1, lsh + n + 1) - lsh - 1;
	for (int i = 1; i <= n; ++i) 
	{
		int t = a[i];
		a[i] = lower_bound(lsh + 1, lsh + cnt + 1, a[i]) - lsh;
		ori[a[i]] = t;  // 
	}		
	// 预处理  
	for (int i = 1; i <= n; ++i) bl[i] = (i - 1) / blo + 1;
	pre(); 
	// 处理询问  
	int x = 0;
	for (int i = 1; i <= m; ++i)
	{
		int l = szread(), r =szread();
		l = ((l + x - 1) % n) + 1;
		r = ((r + x - 1) % n) + 1;
		if (l > r) swap(l, r);
		int val = f[bl[l]+1][bl[r]-1];  // 整块的众数,离散化后的  
		int maxn = upper_bound(v[val].begin(), v[val].end(), r) - lower_bound(v[val].begin(), v[val].end(), l);
		// 计算左边小块的每个数在[l,r]出现的次数 
		for (int j = l; j <= min(bl[l]*blo,n); ++j) 
		{
			int tem = upper_bound(v[a[j]].begin(), v[a[j]].end(), r) - lower_bound(v[a[j]].begin(), v[a[j]].end(), l);
			if (tem > maxn || (tem == maxn && a[j] < val))
			{
				maxn = tem;
				val = a[j];
			}
		}
		// 计算右边的小块  
		if (bl[l] != bl[r])
		{
			for (int j = (bl[r]-1)*blo + 1; j <= r; ++j)
			{
				int tem = upper_bound(v[a[j]].begin(), v[a[j]].end(), r) - lower_bound(v[a[j]].begin(), v[a[j]].end(), l);
				if (tem > maxn || (tem == maxn && a[j] < val))
				{
					maxn = tem;
					val = a[j];
				}
			}
		}
		//
		printf("%d\n", ori[val]);
		x = ori[val];
	} 
	return 0;
}

反思与总结

  1. 在预处理 p r e pre pre函数中,计算f[][]要放在if外面,小细节,很容易忽略。

  2. 离散化后,最后求的结果,要将原始值输出,容易忘记。ori[val]代表离散化后值为val的原始值。

  3. 注意upper_bound()和lower_bound()的使用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值