把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【AT1219】歴史の研究(回滚莫队模板)

点此看题面

  • 给定一个长度为\(n\)的序列。
  • 每次询问给定一个区间,定义一种颜色的价值为它的大小乘上它在这个区间内的出现次数,求所有颜色最大的价值。
  • \(n\le10^5\)

回滚莫队:加入+撤销

这种区间询问的问题一般来说可以利用莫队解决。

然而这道题求的是最大值,似乎普通莫队就无法很好地实现删除操作,因此就需要回滚莫队这种黑科技。

考虑我们先暴力处理掉左端点和右端点在同一个块内的询问(显然这部分复杂度是\(O(n\sqrt n)\)的),然后对于剩余的询问把它扔到左端点对应块的\(vector\)中。

然后,对于每一个左端点对应块,我们把其中的询问按右端点从小到大排序。

对每个询问,我们只要加入上个右端点和当前右端点之间的数,接着加入左端点到块的右边界的所有数,并在询问结束后撤销左端点到块的右边界的修改。

分析复杂度,对于一个块,右端点只会单调右移,总复杂度\(O(n\sqrt n)\);对于每个左端点,到所在块的右边界最多只有\(O(\sqrt n)\)个数,总复杂度也是\(O(n\sqrt n)\)

因此,回滚莫队的复杂度依然是\(O(n\sqrt n)\)的!

这道题的具体实现

加入一个数直接修改计数数组并更新答案即可。

而考虑撤销,只要在加入需要撤销的数(左端点到块的右边界的所有数)之前记录下原先的答案,在撤销时改回答案并直接修改计数数组撤销即可。

应该算是一道比较板子的题目了吧。

代码:\(O(n\sqrt n)\)

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 100000
#define SN 320
#define LL long long
using namespace std;
int n,Qt,a[N+5],dc,dv[N+5],sz,bl[N+5];LL ans[N+5];struct Q
{
	int p,l,r;I Q(CI i=0,CI a=0,CI b=0):p(i),l(a),r(b){}I bool operator < (Con Q& o) Con {return r<o.r;}
};vector<Q> q[SN+5];vector<Q>::iterator it;
namespace FastIO
{
	#define FS 100000
	#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
	#define pc(c) (FC==FE&&(clear(),0),*FC++=c)
	int OT;char c,FI[FS],FO[FS],OS[FS],*FA=FI,*FB=FI,*FC=FO,*FE=FO+FS;
	I void clear() {fwrite(FO,1,FC-FO,stdout),FC=FO;}
	Tp I void read(Ty& x) {x=0;W(!isdigit(c=tc()));W(x=(x<<3)+(x<<1)+(c&15),isdigit(c=tc()));}
	Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
	Tp I void writeln(Ty x) {W(OS[++OT]=x%10+48,x/=10);W(OT) pc(OS[OT--]);pc('\n');}
}using namespace FastIO;
namespace RollBackMo//回滚莫队
{
	LL v;int c[N+5];I void A(CI x) {1LL*++c[x]*dv[x]>v&&(v=1LL*c[x]*dv[x]);}I void D(CI x) {--c[x];}//加入;撤销
	I LL BF(CI l,CI r) {RI i;LL t=v;for(i=l;i<=r;++i) A(a[i]);for(i=r;i>=l;--i) D(a[i]);return swap(v,t),t;}//修改,并在询问后撤销
}using namespace RollBackMo;
int main()
{
	RI i;for(read(n),read(Qt),sz=sqrt(n),i=1;i<=n;++i) read(a[i]),dv[i]=a[i],bl[i]=(i-1)/sz+1;
	for(sort(dv+1,dv+n+1),dc=unique(dv+1,dv+n+1)-dv-1,i=1;i<=n;++i) a[i]=lower_bound(dv+1,dv+dc+1,a[i])-dv;//离散化
	RI x,y;for(i=1;i<=Qt;++i) read(x),read(y),bl[x]^bl[y]?(q[bl[x]].push_back(Q(i,x,y)),0):(v=0,ans[i]=BF(x,y));//同一个块直接暴力
	RI R;for(i=1;i<=bl[n];++i) {sort(q[i].begin(),q[i].end()),R=i*sz,v=0;//初始化块的右端点在块的右边界,清空答案
		for(it=q[i].begin();it!=q[i].end();ans[it->p]=BF(it->l,i*sz),++it) W(R^it->r) A(a[++R]);W(R>i*sz) D(a[R--]);}//单调移动右端点,暴力做左端点
	for(i=1;i<=Qt;++i) writeln(ans[i]);return clear(),0;
}
posted @ 2021-03-07 18:58  TheLostWeak  阅读(111)  评论(0编辑  收藏  举报