bzoj 2120(带修改莫队)

传送门
题解:在普通莫队的基础上再维护一下时间,每次操作将当前时间之后的修改复原,将当前时间之前的修改完成后,再双指针移动即可。
听说块大小为n^(2/3)更快,有待考证(^_^)。

#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e4+2;
int n,m;
int a[MAXN],last[MAXN],cnt[1000002],ans[MAXN],bel[MAXN],tot1=0,tot2=0,siz,ret=0;
bool vis[MAXN];
char opt[10];
struct Query {
    int l,r,id,lst;
    friend bool operator <(const Query &a,const Query &b) {
        if (bel[a.l]^bel[b.l]) return a.l<b.l;
        if (a.r^b.r) return a.r<b.r;
        return a.lst<b.lst;
    }
}q[MAXN];
struct Modify {
    int pos,cur,pre;
}w[MAXN];
inline int read() {
    int x=0;char c=getchar();
    while (c<'0'||c>'9') c=getchar();
    while (c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
    return x;
}
inline void update(int pos) {
    if (vis[pos]) {
        --cnt[a[pos]];
        if (!cnt[a[pos]]) --ret;
    }
    else {
        ++cnt[a[pos]];
        if (cnt[a[pos]]==1) ++ret;
    }
    vis[pos]^=1;//Don't forget this!!!
}
inline void change(int pos,int val) {
    if (vis[pos]) {
        update(pos);
        a[pos]=val;
        update(pos);
    }
    else a[pos]=val;
}
int main() {
//  freopen("bzoj 2120.in","r",stdin);
    memset(vis,false,sizeof(vis));
    memset(cnt,0,sizeof(cnt));
    n=read(),m=read();
//  siz=(int)sqrt(double(n));
    siz=465;//n^(2/3)
    for (int i=1;i<=n;++i) last[i]=a[i]=read(),bel[i]=i/siz;
    for (int i=1;i<=m;++i) {
        scanf("%s",opt);
        if (opt[0]=='Q') {
            q[++tot1].l=read(),q[tot1].r=read();
            q[tot1].id=tot1,q[tot1].lst=tot2;
        }
        else {
            w[++tot2].pos=read();
            w[tot2].pre=last[w[tot2].pos],w[tot2].cur=read();
            last[w[tot2].pos]=w[tot2].cur;
        }
    }
    sort(q+1,q+tot1+1);
    for (int i=1,l=1,r=0;i<=tot1;++i) {
        for (int j=q[i-1].lst+1;j<=q[i].lst;++j) change(w[j].pos,w[j].cur);
        for (int j=q[i-1].lst;j>q[i].lst;--j) change(w[j].pos,w[j].pre);
        while (l<q[i].l) update(l++);
        while (r>q[i].r) update(r--);
        while (l>q[i].l) update(--l);
        while (r<q[i].r) update(++r);
        ans[q[i].id]=ret;
    }
    for (int i=1;i<=tot1;++i) printf("%d\n",ans[i]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值