历史研究

一、题目

点此看题

二、解法

很容易想到普通莫队的解法,为了求最大值,我们需要在跑莫队时维护一个堆,时间复杂度 O ( n n log n ) O(n\sqrt n\log n)

考虑优化,发现我们只能 O ( 1 ) O(1) 处理加入的情况,删除就不能 O ( 1 ) O(1) ,那我们能不能只加入不删除呢?答案是可以的,我们分 n + 1 \sqrt n+1 块来处理,每个块大小为 n + 1 \sqrt n+1 ,我们每一个把左端点放在当前块的最右端,右端点为左端点 1 -1 ,对于块中的每个询问(左端点在块中)我们先跑到右端点,再让左端点从块的右端点出发,跑到询问的左端点,完成这一询问后右端点不变(因为按右端点为第二关键字排序),左端点复位,这样我们就只需要加入操作,时间复杂度 O ( n n ) O(n\sqrt n)

还有一个问题,就是一个询问的左右端点都在块内怎么办,可以预处理这些询问,反正也只花 O ( n ) O(\sqrt n) 的时间。

#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
#define int long long
const int M = 100005;
int read()
{
	int x=0,flag=1;char c;
	while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
	while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
	return x*flag;
}
int n,m,block,Max,a[M],b[M],t[M],ans[M];
struct data
{
	int l,r,id;
	data(int L=0,int R=0,int I=0) : l(L) , r(R) , id(I) {}
	bool operator < (const data &B) const
	{
		if(l/block==B.l/block) return r<B.r;
		return l<B.l;
	}
}q[M];
void add(int x,int f)
{
	t[a[x]]+=f*b[a[x]];//应该加入离散化之前对应的值 
	Max=max(Max,t[a[x]]);
}
signed main()
{
	n=read();m=read();
	block=sqrt(n)+1;//块的大小 
	for(int i=1;i<=n;i++)
		a[i]=b[i]=read();
	sort(b+1,b+1+n);
	int *end=unique(b+1,b+1+n);//去重,返回尾指针 
	for(int i=1;i<=n;i++)
	{
		a[i]=lower_bound(b+1,end,a[i])-b;//得到离散化后的值 
	}
	for(int i=1;i<=m;i++)
	{
		int l=read(),r=read();
		q[i]=data(l,r,i);
	}
	sort(q+1,q+1+m);//莫队排序 
	for(int i=1;i<=m;i++)
		if(q[i].l/block==q[i].r/block)
		{//左右端点同在一块,暴力算 
			for(int j=q[i].l;j<=q[i].r;j++) add(j,1);
			ans[q[i].id]=Max;
			for(int j=q[i].l;j<=q[i].r;j++) add(j,-1);
			Max=0;
		}
	int l=0,r=0;
	for(int i=1;i<=m;i++)
	{
		while(i<=m && q[i].l/block==q[i].r/block) i++;//跳过已经处理的 
		if(i>m) break;
		int ed=(q[i].l/block+1)*block;//算块的右端点 
		if(ed>n) ed=n;
		if(l!=ed)//到了下一个块,清空重新开始 
		{
			l=ed;r=l-1;
			for(int i=1;i<=n;i++) t[i]=0;
			Max=0;
		}
		while(r<q[i].r) add(++r,1);//移动右端点 
		int tmp=Max;//存现在的答案 
		while(q[i].l<l) add(--l,1);//移动左端点 
		ans[q[i].id]=Max;
		while(l<ed) add(l++,-1);//左端点复位 
		Max=tmp;
	}
	for(int i=1;i<=m;i++)
		printf("%lld\n",ans[i]);
}
发布了192 篇原创文章 · 获赞 12 · 访问量 3346

猜你喜欢

转载自blog.csdn.net/C202044zxy/article/details/103834582
今日推荐