让菜鸡讲一讲莫队(带修改)
传说中,莫队算法能解决一切区间处理问题
这是一个优雅的暴力
那么我们再看一道题
[国家集训队]数颜色
膜膜购买了一套N支彩色画笔(其中有些颜色可能相同),摆成一排,你需要回答膜膜的提问。膜膜会像你发布如下指令:
1、 Q L R代表询问你从第L支画笔到第R支画笔中共有几种不同颜色的画笔。
2、 R P Col 把第P支画笔替换为颜色Col。
为了满足膜膜的要求,你知道你需要干什么了吗?
我™当然知道要干什么了
这个比上次那个题多了修改操作!普通莫队已经派不上用场了!
然而普通莫队和带修改莫队没什么区别
当时
提莫莫涛大佬发明了莫队之后,因为有不能修改的debuff,
引起了后人的不爽,
于是后人就发明了带修改莫队。
具体来说,带修改莫队与普通莫队有这个不同:
除了l,r两个指针之外,还增加了指针t。
这个指针的含义是当前序列是在第t次修改后的状态。
同时每个询问也要多记一个东西,就是这个是在第几次修改发生后询问的。
那么询问的排序方法需要改变:
inline const int operator < (const trouble &b)const
{if(cua==b.cua)return r==b.r?t<b.t:r<b.r;return cua<b.cua;}
"即先比较l的块,再比较r,再比较时间"
还要在原来的add,del的基础上多写一个函数,我把它叫做时光机(雾
void tmc(int now,int a)
{
if(q[a].l<=ch[now].d && ch[now].d<=q[a].r)
{
if(--cnt[v[ch[now].d]]==0)num--;
if(cnt[ch[now].to]++==0)num++;
}
swap(v[ch[now].d],ch[now].to);
}
它的作用是把序列的状态变成第now次之后,
同时修改计数君cnt和答案num。
if(q[a].l<=ch[now].d && ch[now].d<=q[a].r)
现在这次修改的地方是ch[now].d,那么只有它在l与r之间的时候才会对cnt产生影响
if(--cnt[v[ch[now].d]]==0)num--;
if(cnt[ch[now].to]++==0)num++;
修改cnt和num
swap(v[ch[now].d],ch[now].to);
把当前颜色和目标颜色交换
为什么是交换?
假设这是t现在的状态
t
1234567... <timeline>
如果现在要把时间变成第5次修改之后,
t
1234567... <timeline>
t到达了5,执行一次tmc(5,...)
,会把修改的那里交换一次;
如果现在又要把时间返回到第4次修改之后,
t
1234567... <timeline>
t离开了5,执行一次tmc(5,...)
,会把修改的那里交换回来。
那么这里是[国家集训队]数颜色的代码,
这也可以作为带修改莫队的模板了。
#include<bits/stdc++.h>
using namespace std;
inline int gotcha()
{
register int a=0,b=1,c=getchar();
while(!isdigit(c))b^=c=='-',c=getchar();
while(isdigit(c))a=(a<<3)+(a<<1)+c-48,c=getchar();
return b?a:-a;
}
const int _ = 10002 , __ = 1000002 , teemo = 133;
struct trouble
{
int l,r,cua,num,t;
inline const int operator < (const trouble &b)const
{if(cua==b.cua)return r==b.r?t<b.t:r<b.r;return cua<b.cua;}
}q[_];
struct changes{int d,to;}ch[_];
int n,m,v[_],cnt[__]={0},num=0,ans[_],qc=0,cc=0;
void add(int d){if(cnt[d]++==0)num++;}void del(int d){if(--cnt[d]==0)num--;}
void tmc(int now,int a)
{
if(q[a].l<=ch[now].d && ch[now].d<=q[a].r)
{if(--cnt[v[ch[now].d]]==0)num--;if(cnt[ch[now].to]++==0)num++;}
swap(v[ch[now].d],ch[now].to);
}
int main()
{
register int i,j,k,l,r,t;
register char op[3];
n=gotcha(),m=gotcha();
for(i=1;i<=n;i++)v[i]=gotcha();
for(i=1;i<=m;i++)
{
scanf("%s",op),j=gotcha(),k=gotcha();
if(op[0]=='Q')q[++qc].l=j,q[qc].r=k,q[qc].cua=q[qc].l/teemo,q[qc].num=qc,q[qc].t=cc;
else ch[++cc].d=j,ch[cc].to=k;
}
sort(q+1,q+qc+1);
l=1,r=0,t=0;
for(i=1;i<=qc;i++)
{
while(l<q[i].l)del(v[l++]);while(l>q[i].l)add(v[--l]);
while(r<q[i].r)add(v[++r]);while(r>q[i].r)del(v[r--]);
while(t<q[i].t)tmc(++t,i);while(t>q[i].t)tmc(t--,i);
ans[q[i].num]=num;
}
for(i=1;i<=qc;i++)printf("%d\n",ans[i]);
return 0;
}