luogu AT1219 歴史の研究 (不删除莫队+离散化)

本文介绍了一种利用不删除莫队(Monotonic Queue)解决区间查询问题的算法。通过保持r值有序,巧妙地按块计算,避免了删除操作的复杂性,时间复杂度降低。主要讲解了如何处理左端点在同一块内的询问,以及如何处理l值的变化和状态维护。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题链:https://www.luogu.com.cn/problem/AT1219

思路:

对于普通莫队,我们一般考虑添加和删除只是顺序变一下,但有的题删除操作根本没办法维护需要的东西。

这时,不删除莫队就可以大显神威了,删除太难了,那我们不删除,只添加就好了。

现在考虑怎么不删除,我们排序时,对于左端点在同一个块内的询问他们的r是从小到大有序的,那么我们可以利用这个特点,一个块一个块的计算询问区间

对于左端点在同一个块内的询问:

首先考虑,如果询问区间的左右端点(ql、qr)都在一个块内,那我们直接暴力计算,复杂度为O(\sqrt{n}),最多m个询问区间的左右端点(ql、qr)都在一个块内,复杂度为O(m\sqrt{n})

否则,我们可以保持r有序,那么对于左端点一个块内的询问,r最坏从头到尾遍历一遍为O(n),总共\sqrt{n}块,复杂度也为O(n\sqrt{n})

对于每个询问,我们现在只要移动l即可,l最多移动\sqrt{n}次,最多m个询问区间,O(m\sqrt{n})

总体复杂度max( O(m\sqrt{n}) , O(n\sqrt{n}))

对于块i中的询问,将l和r初值置为l=br[i]+1,r=br[i]即可。

注意,对于l移动时,带来的修改只对当前询问有关,所以改了要再改回去,有时还要维护两组状态。

注意,这里还有个块0需要带着。

#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 2e5+10;
int n,m,a[N];
set<int> s;
set<int>::iterator it;
unordered_map<int,int> mp;
struct node{
	int l,r,id;
}q[N]; 
int bl[N],br[N],bnum,block,be[N];
bool cmp(node a,node b){
	return (be[a.l]^be[b.l]) ? be[a.l]<be[b.l] : a.r<b.r;
}
ll ans[N];
int cnt[N],cnt1[N];
int main(void){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
		s.insert(a[i]);
	}
	int Id=0;
	for(it=s.begin();it!=s.end();it++)
		mp[(*it)]=++Id;
	block=ceil(sqrt(1.0*n));
	bnum=ceil(1.0*n/block);
	for(int i=1;i<=bnum;i++){
		bl[i]=br[i-1]+1,br[i]=br[i-1]+block;
		for(int j=bl[i];j<=br[i];j++)
			be[j]=i;
	}
	for(int i=1;i<=m;i++)
		scanf("%d%d",&q[i].l,&q[i].r),q[i].id=i;
	sort(q+1,q+1+m,cmp);
	int pos=1;
	ll sum;
	for(int i=0;i<=bnum;i++){
		sum=0;
		memset(cnt,0,sizeof cnt);
		int l=br[i]+1,r=br[i];
		for(;be[q[pos].l]==i;pos++){
			int ql=q[pos].l,qr=q[pos].r;
			if(be[ql]==be[qr]){
				ll temp=0;
				for(int j=ql;j<=qr;j++) cnt1[mp[a[j]]]=0;
				for(int j=ql;j<=qr;j++){
					++cnt1[mp[a[j]]];
					temp=max(1LL*a[j]*cnt1[mp[a[j]]],temp) ;
				}
				ans[q[pos].id]=temp;				
				continue;
			}
			while(r<qr){
				++r;
				++cnt[mp[a[r]]];
				sum=max(sum,1LL*a[r]*cnt[mp[a[r]]]); 
			}
			ll temp=sum;//后面的修改只对这次询问有用
			while(l>ql){
				--l;
				++cnt[mp[a[l]]];
				sum=max(sum,1LL*a[l]*cnt[mp[a[l]]]); 				
			}
			ans[q[pos].id]=sum;
			while(l<br[i]+1){//改回去
				--cnt[mp[a[l]]];
				l++;
			}
			sum=temp;//改回去
		}
	}
	for(int i=1;i<=m;i++)
		printf("%lld\n",ans[i]);
	return 0;	
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值