带修改莫队浅谈
二逼平衡树写得我兹娃乱叫之时,学习了清流的算法——莫队。这次是莫队的进阶版本,资瓷单点修改操作。
原来需要的是两个关键字,而现在多了一维时间。
第一个关键字L表示查询左端点l所在的块的编号,第二个关键字R表示查询右端点r所在块的编号(并不是右端点的值了,因为还有第三维),第三个关键字为时间t。
在这里我们是对修改操作维护的时间轴,之后在查询操作中查找离此次查询操作最近的一次修改操作的时间(请仔细思考)
如果现在的时间比需要的时间大,那么我们让时间倒流,改回原值,剩下的就交给普通莫队了。
我们来对时间复杂度进行证明。
对于一块到底是多少使得时间复杂度最优的问题,我们来分析一下。
以下是luogu题解中的内容对于t轴的移动可以保存每次修改,如果修改在(l,r)间则更新
分块方法可以参照原版莫队,先将l分块,再讲r分块,同一块的按t排序
块大小为
可以达到最快的理论复杂度
,证明如下
设分块大小为a,莫队算法时间复杂度主要为t轴移动,同r块l,r移动,l块间的r移动三部分
t轴移动的复杂度为
,同r块l,r移动复杂度为
,l块间的r移动复杂度为 
三个函数max的最小值当a为
取得,为 
综上所述,一块大小为√n3。
我们直接用STL解决
size=pow(n,0.67);
HH的项链的进阶版,多了个单点修改
// luogu-judger-enable-o2
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
#define N 50010
int size;
int n,Q;
struct Query
{
int blobx,bloby,x,y,daan,id,pre;
}query[N];
struct Change
{
int pos,color;
}change[N];
int a[N];
int col[N];
int sum[1000100];
int ans;
char q[3];
int idx,idx2;
int x,y;
bool cmp(const Query &a,const Query &b)
{
if(a.blobx!=b.blobx)
return a.blobx<b.blobx;
if(a.bloby!=b.bloby)
return a.bloby<b.bloby;
return a.id<b.id;
}
bool cmp2(const Query &a,const Query &b)
{
return a.id<b.id;
}
int calc(int x)
{
if(x%size==0)
return x/size;
else
return x/size+1;
}
void del(int x)
{
if(--sum[a[x]]==0)
ans--;
}
void add(int x)
{
if(++sum[a[x]]==1)
ans++;
}
void go(int now,int i)
{
if(change[now].pos>=query[i].x&&change[now].pos<=query[i].y)
{
if(--sum[a[change[now].pos]]==0)
ans--;
if(++sum[change[now].color]==1)
ans++;
}
swap(a[change[now].pos],change[now].color);
}
int main()
{
scanf("%d%d",&n,&Q);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
size=pow(n,0.67);
for(int i=1;i<=Q;i++)
{
scanf("%s",q);
if(q[0]=='Q')
{
scanf("%d%d",&x,&y);
query[++idx].x=x;
query[idx].y=y;
query[idx].blobx=calc(query[idx].x);
query[idx].bloby=calc(query[idx].y);
query[idx].id=idx;
query[idx].pre=idx2;
}
else
{
scanf("%d%d",&x,&y);
change[++idx2].pos=x;
change[idx2].color=y;
}
}
sort(query+1,query+idx+1,cmp);
int l=1,r=0,tim=0;
for(int i=1;i<=idx;i++)
{
while(l<query[i].x)
del(l++);
while(l>query[i].x)
add(--l);
while(r<query[i].y)
add(++r);
while(r>query[i].y)
del(r--);
while(tim<query[i].pre)
go(++tim,i);
while(tim>query[i].pre)
go(tim--,i);
query[i].daan=ans;
}
sort(query+1,query+idx+1,cmp2);
for(int i=1;i<=idx;i++)
printf("%d\n",query[i].daan);
}

浙公网安备 33010602011771号