歴史の研究(回滚)
歴史の研究
题面翻译
题目描述
IOI 国历史研究的第一人——JOI 教授,最近获得了一份被认为是古代 IOI 国的住民写下的日记。JOI 教授为了通过这份日记来研究古代 IOI 国的生活,开始着手调查日记中记载的事件。
日记中记录了连续
事件有种类之分。第
表示,
JOI 教授决定用如下的方法分析这些日记:
-
选择日记中连续的几天
作为分析的时间段; -
定义事件
的重要度 为 ,其中 为该事件在区间 中出现的次数。
现在,您需要帮助教授求出所有事件中重要度最大的事件是哪个,并输出其重要度。
注意:教授有多组询问。
输入格式
第一行两个空格分隔的整数
接下来一行
接下来
输出格式
输出共有
数据范围
对于
样例输入 #1
5 5
9 8 7 8 9
1 2
3 4
4 4
1 4
2 4
样例输出 #1
9
8
8
16
16
样例 #2
样例输入 #2
8 4
9 9 19 9 9 15 9 19
1 4
4 6
3 5
5 8
样例输出 #2
27
18
19
19
样例 #3
样例输入 #3
12 15
15 9 3 15 9 3 3 8 16 9 3 17
2 7
2 5
2 2
1 12
4 12
3 6
11 12
1 7
2 6
3 5
3 10
7 10
1 4
4 8
4 8
样例输出 #3
18
18
9
30
18
15
17
30
18
15
18
16
30
15
15
解析
复习回滚莫队板子,
正常莫队有两种操作,
以本题为例,题意为求一个区间中一个数乘以它的出现次数的最大值。
也就是求
这时
但是
我们需要记录很多很多组。
因此为了避免
进行分块后我们按照左端点整块排序,同一块内的按右端点排序。
对同一块进行讨论:
-
和 在同一块内,暴力求解。 -
和 不在同一块内,因为排过序,所以 一定是单调递增的。
但是 的顺序不确定,我们选取块右端点作为 基点 ,每次先动 ,然后记录 基点 到当前 的 .
动完 在把刚才记录的 腾过来。
code
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e5+5;
int n,m;
int a[N],cnt[N],cntb[N],f[N],tot;
LL tmp,ans[N];
int l,r;
map<int,int> mp;
struct Q
{
int l,r,id;
} q[N];
int L[4000],R[4000],bl[N];
void bui()
{
int cnt = sqrt(n);
for(int i=1;i<=cnt;i++)
{
L[i]=n/cnt*(i-1)+1;
R[i]=n/cnt*i;
}
R[cnt]=n;
for(int i=1;i<=cnt;i++)
{
for(int j=L[i];j<=R[i];j++)
{
bl[j]=i;
}
}
}
void add(int x)
{
cnt[x]++;
tmp=max((LL)cnt[x]*f[x],tmp);
}
bool operator < (Q &x,Q &y)
{
if(bl[x.l]!=bl[y.l]) return bl[x.l]<bl[y.l];
else return x.r<y.r;
}
void mo_q()
{
for(int i=1;i<=m;)
{
memset(cnt,0,sizeof(cnt)); tmp=0;
int j=i; int right=bl[q[i].l];
while(right==bl[q[j].l]&&j<=m) j++;//找到左端点在同一块内的
r=R[right]; l=r+1;
while(i<j)
{
if(q[i].r<=R[right])//同一块内
{
for(int k=q[i].l;k<=q[i].r;k++) add(a[k]);
ans[q[i].id]=tmp;
tmp=0; memset(cnt,0,sizeof(cnt));
i++;
}
else//不同块
{
while(r<q[i].r) add(a[++r]);
memcpy(cntb,cnt,sizeof(cnt));
LL bbb=tmp;
while(l>q[i].l) add(a[--l]);
ans[q[i].id]=tmp; i++;
tmp=bbb;
l=R[right]+1;
memcpy(cnt,cntb,sizeof(cntb));
}
}
}
}
int main()
{
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
if(mp[a[i]]==0)
{
mp[a[i]]=++tot;
f[tot]=a[i];
a[i]=tot;
}
else a[i]=mp[a[i]];
}
bui();//注意调用
for(int i=1;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
q[i]=Q{x,y,i};
}
sort(q+1,q+1+m);
mo_q();//注意调用
for(int i=1;i<=m;i++) printf("%lld\n",ans[i]);
return 0;
}
例题
思路类似
code
#include<bits/stdc++.h>
using namespace std;
const int N = 50005;
int n,m;
int a[N];
int L[300],R[300],bl[N];
int up[N],down[N],bdown[N],bup[N],ans[N];
int num,b;
struct Q
{
int l,r,id;
} q[N];
void bui()
{
int cnt=sqrt(n);
for(int i=1;i<=cnt;i++)
{
L[i]=n/cnt*(i-1)+1;
R[i]=n/cnt*i;
}
R[cnt]=n;
for(int i=1;i<=cnt;i++)
{
for(int j=L[i];j<=R[i];j++)
{
bl[j]=i;
}
}
}
void add(int x)
{
up[x]=up[x-1]+1;
down[x]=down[x+1]+1;
int t=up[x]+down[x]-1;
down[x-up[x]+1]=t;
up[x+down[x]-1]=t;
num=max(t,num);
}
bool cmp(Q &x,Q &y)
{
if(bl[x.l]==bl[y.l]) return x.r<y.r;
return bl[x.l]<bl[y.l];
}
void mo_q()
{
for(int i=1;i<=m;)
{
int j=i;
while(j<=m&&bl[q[i].l]==bl[q[j].l]) j++;
int now=R[bl[q[i].l]];
while(i<j&&q[i].r<=now)
{
for(int k=q[i].l;k<=q[i].r;k++) add(a[k]);
ans[q[i].id]=num;
num=0;
memset(up,0,sizeof(up));
memset(down,0,sizeof(down));
i++;
}
int r=now,l=now+1;
while(i<j)
{
while(r<q[i].r) add(a[++r]);
memcpy(bup,up,sizeof(up));
memcpy(bdown,down,sizeof(down));
b=num;
while(l>q[i].l) add(a[--l]);
ans[q[i].id]=num;
memcpy(up,bup,sizeof(up));
memcpy(down,bdown,sizeof(down));
num=b; l=now+1;
i++;
}
memset(up,0,sizeof(up));
memset(down,0,sizeof(down));
num=0;
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
bui();
for(int i=1;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
q[i]={x,y,i};
}
sort(q+1,q+1+m,cmp);
mo_q();
for(int i=1;i<=m;i++) printf("%d\n",ans[i]);
return 0;
}
注意
莫队思路好想,方法暴力,熟悉暴力的打法吧。
重置状态时需要精细处理维护复杂度,上述都是粗糙处理,看个乐子,不能保证复杂度。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」