Dynamic len

题目描述

有n个数编号从0→n-1,两种操作:
Q L R:询问编号为L→R-1的数中共有多少种不同的数
M X Y:将编号为X的数改为Y
共有m个操作

数据范围

n,m<=50000
数的范围<=1000000

树状数组+主席树,可行,但好难打也

莫队算法,短,刚好能碾过

带修改的莫队比不带修改的莫队多了一维,表示该询问操作前有多少个修改操作
预处理每个修改操作,序列一直更新,记录每个修改操作的前值和后值,
排序按照第一关键字为l的所属块,第二关键字为r的所属块,第三关键字为该操作前有多少个修改操作,
然后就像普通莫队一样搞一搞。
不过刚好能碾过需要一定技巧,比如块的个数不宜过多,所以块的大小要大一些。

代码

#include
#include
#include
#include
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=100000+5;
int a[maxn],i,j,n,m,b[maxn],qt,t,a0[maxn],ans[maxn],now,d[maxn];
struct ar{
    int x,y,z,d;
} q[maxn];
struct arr{
    int x,y,h;
} ca[maxn];
bool cmp(ar x,ar y){
    return ((b[x.x]y.x])||(b[x.x]==b[y.x]&&b[x.y]y.y])||((b[x.x]==b[y.x]&&b[x.y]==b[y.y])&&x.z<y.z));
}
void add(int x){
    now+=!d[x]++;
}
void era(int x){
    now-=!--d[x];
}
void change(int x,int l,int r,bool bz)
{
    int y=ca[x].x;
    if (l<=y&&y<=r) era(a[y]);
    a[y]=bz?ca[x].h:ca[x].y;
    if (l<=y&&y<=r) add(a[y]);
}
int main(){
    freopen("len.in","r",stdin);
    freopen("len.out","w",stdout);
    scanf("%d%d",&n,&m);
    int k=1000;
    t=1;
    fo(i,1,n) {
        scanf("%d",&a[i]);
        a0[i]=a[i];
        b[i]=t;if (i%k==0) t++;
    }
    qt=t=0;
    fo(i,1,m) {
        char c=getchar();
        while ((c<'A')||(c>'Z')) c=getchar();
        int x,y;
        scanf("%d%d",&x,&y);x++;
        if (c=='Q') q[++qt].x=x,q[qt].y=y,q[qt].z=t,q[qt].d=qt;else
        ca[++t].x=x,ca[t].y=a[x],a[x]=ca[t].h=y;
    }
    fo(i,1,n) a[i]=a0[i];
    sort(q+1,q+qt+1,cmp);
    int l=q[1].x,r=q[1].y,t=q[1].z;
    fo(i,l,r) add(a[i]);
    fo(i,1,t) change(i,l,r,1);
    ans[q[1].d]=now;
    fo(i,2,qt){
        int l1=q[i].x,r1=q[i].y,t1=q[i].z;
        while (l>l1) add(a[--l]);
        while (lwhile (rwhile (r>r1) era(a[r--]);
        while (t1);
        while (t>t1) change(t--,l,r,0);
        ans[q[i].d]=now;
    }
    fo(i,1,qt) printf("%d\n",ans[i]);
}

你可能感兴趣的:(莫队算法)