2523. 历史研究

题目链接

2523. 历史研究

IOI 国历史研究的第一人——JOI 教授,最近获得了一份被认为是古代 IOI 国的住民写下的日记。

JOI 教授为了通过这份日记来研究古代 IOI 国的生活,开始着手调查日记中记载的事件。

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

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

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

  1. 选择日记中连续的一些天作为分析的时间段
  2. 事件种类 \(t\) 的重要度为 \(t \times\) (这段时间内重要度为 \(t\) 的事件数)
  3. 计算出所有事件种类的重要度,输出其中的最大值

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

输入格式

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

接下来一行 \(N\) 个空格分隔的整数 \(X_1…X_N\)\(X_i\) 表示第 \(i\) 天发生的事件的种类。

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

输出格式

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

数据范围

\(1 \le N \le 10^5\),
\(1 \le Q \le 10^5\),
\(1 \le X_i \le 10^9\)

输入样例:

5 5
9 8 7 8 9
1 2
3 4
4 4
1 4
2 4

输出样例:

9
8
8
16
16

解题思路

回滚莫队

排序方式同普通莫队,即将 \(l\) 分块,每块内 \(r\) 递增,每块长度为 \(\sqrt{n}\),每次操作都是一块一块操作,先暴力处理块内的询问,每次其复杂度为 \(\sqrt{n}\),然后处理块间的询问,由于一个块内的 \(r\) 指针递增,即处理询问时 \(r\) 指针递增,而 \(l\) 不定,但 \(l\) 指针始终在块内,所以处理 \(l\) 指针可以暴力

  • 时间复杂度:\(O(n\sqrt{n})\)

代码

// Problem: 历史研究
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/2525/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

// %%%Skyqwq
#include <bits/stdc++.h>
 
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
 
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
 
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
 
template <typename T> void inline read(T &x) {
    int f = 1; x = 0; char s = getchar();
    while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
    while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
    x *= f;
}

const int N=1e5+5;
int n,m,w[N],cnt[N],len;
LL ans[N];
vector<int> nums;
struct query
{
	int id,l,r;
}q[N];
int get(int x)
{
	return x/len;
}
void add(int x,LL &res)
{
	cnt[x]++;
	res=max(res,(LL)nums[x]*cnt[x]);
}
void del(int x)
{
	cnt[x]--;
}
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)cin>>w[i],nums.pb(w[i]);
	sort(nums.begin(),nums.end());
	nums.erase(unique(nums.begin(),nums.end()),nums.end());
	for(int i=1;i<=n;i++)w[i]=lower_bound(nums.begin(),nums.end(),w[i])-nums.begin();
	len=sqrt(n);
	for(int i=1;i<=m;i++)
	{
		q[i].id=i;
		cin>>q[i].l>>q[i].r;
	}
	sort(q+1,q+1+m,[](const query &a,const query &b){
		int al=get(a.l),bl=get(b.l);
		if(al!=bl)return al<bl;
		return a.r<b.r;
	});
	for(int x=1;x<=m;)
	{
		int y=x;
		while(y<=m&&get(q[y].l)==get(q[x].l))y++;
		int right=get(q[x].l)*len+len-1;
		while(x<y&&q[x].r<=right)
		{
			int l=q[x].l,r=q[x].r,id=q[x].id;
			LL res=0;
			for(int i=l;i<=r;i++)add(w[i],res);
			ans[id]=res;
			for(int i=l;i<=r;i++)del(w[i]);
			x++;
		}
		LL res=0;
		int i=right,j=right+1;
		while(x<y)
		{
			int l=q[x].l,r=q[x].r,id=q[x].id;
			while(i<r)add(w[++i],res);
			LL backup=res;
			while(j>l)add(w[--j],res);
			ans[id]=res;
			while(j<right+1)del(w[j++]);
			res=backup;
			x++;
		}
		memset(cnt,0,sizeof cnt);
	}
	for(int i=1;i<=m;i++)cout<<ans[i]<<'\n';
    return 0;
}
posted @ 2022-10-08 13:01  zyy2001  阅读(45)  评论(0编辑  收藏  举报