带修改莫队
Ender_NaCl · · 个人记录
简介
一般莫队是不支持修改的,所以我们需要对普通莫队进行一些修改,让它支持修改。
没学过普通莫队算法的建议先学普通莫队算法
主要思想
莫队的移动
主要讲一讲和普通莫队不一样的地方,也就是带修莫队增加的地方
普通莫队只有
那么每个询问就拥有三个属性
如果现在所在的点是
然后每动一步就进行一次更改操作
先从一道例题 P1903 [国家集训队] 数颜色 / 维护队列 入手
假如用
while(CL > l) Add(a[--CL]);
while(CR < r) Add(a[++CR]); //普通莫队操作
while(CL < l) Delete(a[CL++]);
while(CR > r) Delete(a[CR--]);
while(CT < t) Upd(++CT,i); //改少了就改过去
while(CT > t) Upd(CT--,i); //改多了就改回来
然后排序有三个关键字,即排序优先级从上到下
- 询问的左端点所在块为第一关键字
- 询问的右端点所在块为第二关键字
- 询问的
t 所在块为第三关键字
代码
上本题代码
#include <iostream>
#include <cmath>
#include <algorithm>
using namespace std;
const int N = 133343;
const int M = 1000010;
struct Q //询问
{
int l,r,t,id;
}q[N];
struct Change //更改
{
int num,col;
}change[N];
int posi[N],a[N],t[M],ans[N];
int n,m,qnum,rnum,sum,sq; //qnum,rnum存储查询和修改的次数
const bool cmp(const Q& x,const Q& y)
{
if(posi[x.l] != posi[y.l]) return posi[x.l] < posi[y.l];
else if(posi[x.r] != posi[y.r]) return posi[x.r] < posi[y.r];
else return x.t < y.t;
}
void Add(int x) //添加某颜色
{
if(t[x] == 0) sum++;
t[x]++;
}
void Delete(int x) //删除某颜色
{
t[x]--;
if(t[x] == 0) sum--;
}
void Upd(int x,int id) //第x次更改,第id次询问
{
if(change[x].num >= q[id].l&&change[x].num <= q[id].r) //如果即将更改的数在查询区间内,那就会对结果造成影响
{
Delete(a[change[x].num]); //删除原数
Add(change[x].col); //添加新颜色
}
swap(a[change[x].num],change[x].col); //第一次变成新颜色,第二次旧颜色,第三次新颜色......以此类推,每次都会更新或还原
}
void MO()
{
int i,CL = 1,CR = 0,CT = 0;
for(i = 1;i <= qnum;i++)
{
int l = q[i].l,r = q[i].r,t = q[i].t;
while(CL > l) Add(a[--CL]);
while(CR < r) Add(a[++CR]); //普通莫队操作
while(CL < l) Delete(a[CL++]);
while(CR > r) Delete(a[CR--]);
while(CT < t) Upd(++CT,i); //改少了就改过去
while(CT > t) Upd(CT--,i); //改多了就改回来
ans[q[i].id] = sum;
}
}
int main()
{
int i;
cin>>n>>m;
sq = pow(n,2.0 / 3.0); //块长
for(i = 1;i <= n;i++) cin>>a[i];
for(i = 1;i <= n;i++) posi[i] = (i - 1) / sq + 1; //分块
for(i = 1;i <= m;i++)
{
char op;
cin>>op;
if(op == 'Q')
{
qnum++;
cin>>q[qnum].l>>q[qnum].r;
q[qnum].t = rnum; //已经修改的次数
q[qnum].id = qnum;
}
else
{
rnum++;
cin>>change[rnum].num>>change[rnum].col;
}
}
sort(q + 1,q + qnum + 1,cmp);
MO();
for(i = 1;i <= qnum;i++) cout<<ans[i]<<endl;
return 0;
}
完结撒花