2523. 历史研究
题目链接
2523. 历史研究
IOI 国历史研究的第一人——JOI 教授,最近获得了一份被认为是古代 IOI 国的住民写下的日记。
JOI 教授为了通过这份日记来研究古代 IOI 国的生活,开始着手调查日记中记载的事件。
日记中记录了连续 \(N\) 天发生的时间,大约每天发生一件。
事件有种类之分。第 \(i\) 天 \((1 \le i \le N)\) 发生的事件的种类用一个整数 \(X_i\) 表示,\(X_i\) 越大,事件的规模就越大。
JOI 教授决定用如下的方法分析这些日记:
- 选择日记中连续的一些天作为分析的时间段
- 事件种类 \(t\) 的重要度为 \(t \times\) (这段时间内重要度为 \(t\) 的事件数)
- 计算出所有事件种类的重要度,输出其中的最大值
现在你被要求制作一个帮助教授分析的程序,每次给出分析的区间,你需要输出重要度的最大值。
输入格式
第一行两个空格分隔的整数 \(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;
}