[BZOJ 4241] 历史研究

BZOJ传送门

题目描述

IOI国历史研究的第一人——JOI教授,最近获得了一份被认为是古代IOI国的住民写下的日记。JOI教授为了通过这份日记来研究古代IOI国的生活,开始着手调查日记中记载的事件。

日记中记录了连续 N N N天发生的时间,大约每天发生一件。

事件有种类之分。第 i i i ( 1 ≤ i ≤ N ) (1\le i\le N) (1iN)发生的事件的种类用一个整数 X i X_i Xi表示, X i X_i Xi越大,事件的规模就越大。

JOI教授决定用如下的方法分析这些日记:

  1. 选择日记中连续的一些天作为分析的时间段

  2. 事件种类 t t t的重要度为 t t t*(这段时间内重要度为 t t t的事件数)

  3. 计算出所有事件种类的重要度,输出其中的最大值

现在你被要求制作一个帮助教授分析的程序,每次给出分析的区间,你需要输出重要度的最大值。

输入输出格式

输入格式

第一行两个空格分隔的整数 N N N Q Q Q,表示日记一共记录了 N N N天,询问有 Q Q Q次。

接下来一行 N N N个空格分隔的整数 X 1 . . . X N X_1...X_N X1...XN X i X_i Xi表示第 i i i天发生的事件的种类

接下来 Q Q Q行,第 i i i ( 1 ≤ i ≤ Q ) (1\le i\le Q) (1iQ)有两个空格分隔整数 A i A_i Ai B i B_i Bi,表示第 i i i次询问的区间为 [ A i , B i ] [A_i,B_i] [Ai,Bi]

输出格式

输出 Q Q Q行,第 i i i ( 1 ≤ i ≤ Q ) (1\le i\le Q) (1iQ)一个整数,表示第 i i i次询问的最大重要度

输入输出样例

输入样例#1:
5 5
9 8 7 8 9
1 2
3 4
4 4
1 4
2 4
输出样例#1:
9
8
8
16
16

解题分析

如果用普通莫队在删除时无法维护次大值, 需要一个 s e t set set, 就GG了。

所以去学了新姿势: 回滚莫队。

还是分块, 把左端点在一块的询问按右端点从小到大排序。

如果左右端点都在一块先处理掉。

然后我们只维护左端点在当前左端点所在块最右端, 且右端点单调递增的答案, 每次动态插入左边块内的元素统计答案后再撤销。

总复杂度还是 Q N Q\sqrt N QN 的。

代码如下:

#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <cctype>
#include <vector>
#include <tr1/unordered_map>
#include <algorithm>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define MX 100500
#define ll long long
template <class T>
IN void in(T &x)
{
	x = 0; R char c = gc;
	for (; !isdigit(c); c = gc);
	for (;  isdigit(c); c = gc)
	x = (x << 1) + (x << 3) + c - 48;
}
template <class T> IN T max(T a, T b) {return a > b ? a : b;}
std::tr1::unordered_map <int, int> mp;
int n, m, sqr, qcnt, arr, bcnt;
ll ans[MX];
int blk[MX], val[MX], cnt[MX], id[MX];
struct Query {int l, r, id;};
IN bool operator < (const Query &x, const Query &y) {return x.r < y.r;}
std::vector <Query> que[500];
int main(void)
{
	int foo, bar, cur, siz;
	R ll rem, mx;
	in(n), in(m); sqr = std::sqrt(n); bcnt = n / sqr;
	for (R int i = 1; i <= n; ++i)
	{
		in(foo), blk[i] = i / sqr;
		if (!mp[foo]) mp[foo] = ++arr, val[arr] = foo;
		id[i] = mp[foo];
	}
	for (R int i = 1; i <= m; ++i)
	{
		in(foo), in(bar);
		if (blk[foo] == blk[bar])
		{
			mx = 0;
			for (R int j = foo; j <= bar; ++j)
			{
				++cnt[id[j]];
				mx = max(1ll * cnt[id[j]] * val[id[j]], mx);
			}
			ans[i] = mx; mx = 0;
			for (R int j = foo; j <= bar; ++j) --cnt[id[j]];
		}
		else que[foo / sqr].push_back({foo, bar, i});
	}
	std::sort(que + 1, que + 1 + qcnt);
	for (R int i = 0; i <= bcnt; ++i)
	{
		std::sort(que[i].begin(), que[i].end());
		std::memset(cnt, mx = 0, sizeof(cnt));
		cur = (i + 1) * sqr - 1, siz = que[i].size();
		for (R int j = 0; j < siz; ++j)
		{
			W (cur < que[i][j].r)
			{
				++cur;
				++cnt[id[cur]];
				mx = max(mx, 1ll * cnt[id[cur]] * val[id[cur]]);
			}
			rem = mx;
			for (R int k = (i + 1) * sqr - 1; k >= que[i][j].l; --k)
			{
				++cnt[id[k]];
				mx = max(mx, 1ll * cnt[id[k]] * val[id[k]]);
			}
			ans[que[i][j].id] = mx;
			for (R int k = (i + 1) * sqr - 1; k >= que[i][j].l; --k) --cnt[id[k]];
			mx = rem;
		}
	}
	for (R int i = 1; i <= m; ++i) printf("%lld\n", ans[i]);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值