歴史の研究(回滚)

歴史の研究

题面翻译

题目描述

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

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

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

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

  • 选择日记中连续的几天 \([L,R]\) 作为分析的时间段;

  • 定义事件 \(A\) 的重要度 \(W_A\)\(A\times T_A\),其中 \(T_A\) 为该事件在区间 \([L,R]\) 中出现的次数。

现在,您需要帮助教授求出所有事件中重要度最大的事件是哪个,并输出其重要度

注意:教授有多组询问。

输入格式

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

接下来一行 \(N\) 个空格分隔的整数表示每天的事件种类。

接下来 \(Q\) 行,每行给出 \(L,R\) 表示一组询问。

输出格式

输出共有 \(Q\) 行,每行一个整数,表示对应的询问的答案。

数据范围

对于 \(100\%\) 的数据,\(1\le Q,N\le 10^5\)\(1\le X\le 10^9\)\(1\le L\le R\le 10^5\)

样例输入 #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

解析

复习回滚莫队板子,

正常莫队有两种操作, \(add\)\(del\) ,而回滚莫队用于处理一些只能进行其中一种操作的。

以本题为例,题意为求一个区间中一个数乘以它的出现次数的最大值。

也就是求 \(x \times cnt[x]\) 的最大值。

这时 \(add\) 操作很好实现,对于新加入的数更新 \(cnt\) 并求值,取最大值。

但是 \(del\) 操作很不好实现,因为删除一个数以后最大值可能不变,也可能变为次大值。

我们需要记录很多很多组。

因此为了避免 \(del\) 操作,我们选用回滚莫队。

进行分块后我们按照左端点整块排序,同一块内的按右端点排序。

对同一块进行讨论:

  1. \(l\)\(r\) 在同一块内,暴力求解。

  2. \(l\)\(r\) 不在同一块内,因为排过序,所以 \(r\) 一定是单调递增的。
    但是 \(l\) 的顺序不确定,我们选取块右端点作为 基点 ,每次先动\(r\) ,然后记录 基点 到当前 \(r\)\(cnt\).
    动完 \(l\) 在把刚才记录的 \(cnt\) 腾过来。

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;
}

注意

莫队思路好想,方法暴力,熟悉暴力的打法吧。

\(\mathbb{mo \_ q}\)

posted @ 2024-05-03 21:47  ppllxx_9G  阅读(18)  评论(0编辑  收藏  举报