链接
https://www.luogu.org/problem/P1903
题意
给定一个长度为n的序列,m次操作。需要支持两种操作:
1:把位置i上的数修改为v(从1开始编号)。
2:查询[L,R]内有多少种不同的数。
分析
这是一道带修莫队的模板题。
每个查询操作除了有左端点l和右端点r,还有一个时间t,
表示这个查询在第t个修改操作之后。
将这些查询(l,r,t)进行排序,以l/S为第一关键字,r/S为第二关键字,t为第三关键字。
其中S为分块的大小。
与普通莫队相比,当从上一个询问跳到当前的询问时,还要维护目前的修改数now。
当now小于查询的修改数时,就把没有进行的修改操作执行。
当now大于查询的修改数时,就将多出的已执行的修改进行回撤。
需要注意的是,如果执行或回撤的修改操作在目前的区间内,就要更新答案。
在修改数now与查询的修改数相等之后,再进行左右端点的移动。
复杂度分析:
n为序列长度,c为修改操作数,q为查询操作数,S为分块的大小。
- 修改数的移动:
左端点分成n/S块,右端点分成n/S块,每个左右端点块最多移动c。
因此移动次数为 O ( c ∗ n 2 / S 2 ) O(c*n^2/S^2) O(c∗n2/S2) - 左端点的移动:
在同一左端点块中,每次最多移动S;
到下一左端点块时,最多移动2S。
因此移动次数为 O ( q S ) O(qS) O(qS) - 右端点的移动:
在同一右端点块中,每次最多移动S;
到下一右端点块时,每次最多移动2S;
当左端点改变时,每次最多移动n。
因此移动次数为 O ( q S + n 2 / S ) O(qS+n^2/S) O(qS+n2/S)。
于是总移动次数为
O
(
q
S
+
c
∗
n
2
/
S
2
+
n
2
/
S
)
O(qS+c*n^2/S^2+n^2/S)
O(qS+c∗n2/S2+n2/S)。
由于不知道c和q的值,那么c和q统一用m代替,且假设n=m,
那么总移动次数就成了
O
(
q
S
+
n
3
/
S
2
+
n
2
/
S
)
O(qS+n^3/S^2+n^2/S)
O(qS+n3/S2+n2/S)。
当
S
=
n
2
/
3
S=n^{2/3}
S=n2/3时,最优复杂度为
n
5
/
3
n^{5/3}
n5/3。
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double db;
template<class T>inline void MAX(T &x,T y){if(y>x)x=y;}
template<class T>inline void MIN(T &x,T y){if(y<x)x=y;}
template<class T>inline void rd(T &x){
x=0;char o,f=1;
while(o=getchar(),o<48)if(o==45)f=-f;
do x=(x<<3)+(x<<1)+(o^48);
while(o=getchar(),o>47);
x*=f;
}
const int M=140005;
const int S=2500;
const int K=1e6+5;
struct node{
int l,r,t,id;
bool operator <(const node &A)const{
if(l/S!=A.l/S)return l/S<A.l/S;
if(r/S!=A.r/S){
if(l/S&1)return r/S<A.r/S;
else return r/S>A.r/S;
}
if(r/S&1)return t<A.t;
else return t>A.t;
}
}Q[M];
char str[M][5];
int n,m,A[M],pos[M],X[M],Y[M],ans[M];
int res,cnt[K];
void insert(int x){
cnt[x]++;
if(cnt[x]==1)res++;
}
void erase(int x){
cnt[x]--;
if(cnt[x]==0)res--;
}
int main(){
#ifndef ONLINE_JUDGE
freopen("jiedai.in","r",stdin);
// freopen("jiedai.out","w",stdout);
#endif
rd(n),rd(m);
for(int i=1;i<=n;i++)rd(A[i]);
int l=1,r=0,now=0,tot=0;
for(int i=1;i<=m;i++){
scanf("%s",str[i]);
int a,b;
rd(a),rd(b);
if(str[i][0]=='R'){
now++;
pos[now]=a;
X[now]=A[a];
Y[now]=b;
A[a]=b;
}
else Q[++tot]=(node){a,b,now,i};
}
sort(Q+1,Q+1+tot);
for(int i=1;i<=tot;i++){
while(now<Q[i].t){
now++;
A[pos[now]]=Y[now];
if(pos[now]>=l&&pos[now]<=r)erase(X[now]),insert(Y[now]);
}
while(now>Q[i].t){
A[pos[now]]=X[now];
if(pos[now]>=l&&pos[now]<=r)erase(Y[now]),insert(X[now]);
now--;
}
while(r<Q[i].r)insert(A[++r]);
while(l>Q[i].l)insert(A[--l]);
while(r>Q[i].r)erase(A[r--]);
while(l<Q[i].l)erase(A[l++]);
ans[Q[i].id]=res;
}
for(int i=1;i<=m;i++)if(str[i][0]=='Q')printf("%d\n",ans[i]);
return (0-0);
}