数颜色

     题意:

  n个数,m个操作。

  •  查询区间[a,b]内不同数字数
  •  将某个数修改为c

  题解:

  带修改莫队。

    与普通莫队不同的就是要记录一下每个查询操作前有多少个修改操作,然后暴力修改或改回去。

 

  题目链接:https://www.luogu.org/problemnew/show/P1903

 

 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define LL long long
#define RI register int
using namespace std;
const int INF = 0x7ffffff ;
const int S = 1e6 + 10 ;
const int N = 10000 + 10 ;

inline int read() {
    int k = 0 , f = 1 ; char c = getchar() ;
    for( ; !isdigit(c) ; c = getchar())
      if(c == '-') f = -1 ;
    for( ; isdigit(c) ; c = getchar())
      k = k*10 + c-'0' ;
    return k*f ;
}

struct Que {
    int l, r, ti, id ;
}q[N] ;

int n, m, qcnt = 0, rcnt = 0, hh[N], pp[1010], cc[1010], res[N], pos[N], num[S], ans = 0 ;
int L = 1, R = 0, T = 0 ;

inline bool cmp1(Que s,Que t) {  // 其实我也不知道怎么排序最快qaq 
    if(pos[s.l] == pos[t.l]) {
        if(pos[s.r] == pos[t.r]) return s.ti < t.ti ;
        return s.r < t.r ;
    } 
    return s.l < t.l ;
}

inline void add(int x) {
    if(!num[hh[x]]) ans ++ ;
    num[hh[x]] ++ ;
}
inline void del(int x) {
    num[hh[x]] -- ;
    if(!num[hh[x]]) ans -- ;
}
inline void addt(int t) {
    if(pp[t] >= L && pp[t] <= R) {
        num[hh[pp[t]]] -- ;
        if(!num[hh[pp[t]]]) ans -- ;
        if(!num[cc[t]]) ans ++ ;
        num[cc[t]] ++ ;
    }
    swap(hh[pp[t]],cc[t]) ;
    // 这是一步很神奇的操作
    // 因为记录每个位置的历史值显然是十分麻烦的。
    // 我们可以观察一个性质就是当一个修改操作被改回去时,就是原值变成修改值,修改值变成原值
    // 所以只要交换一下即可 
}

int main() {
    n = read(), m = read() ;
    int base = sqrt(n) ;
    for(int i=1;i<=n;i++) hh[i] = read(), pos[i] = (i-1)/base + 1 ;
    char cs ;
    for(int i=1;i<=m;i++) {
        cin>>cs ;
        if(cs == 'Q') {
            q[++qcnt].l = read(), q[qcnt].r = read() ;
            q[qcnt].ti = rcnt ; q[qcnt].id = qcnt ;
        } else {
            pp[++rcnt] = read(), cc[rcnt] = read() ;
        }
    }
    sort(q+1,q+qcnt+1,cmp1) ;
    for(int i=1;i<=qcnt;i++) {
        while(L < q[i].l) del(L++) ;
        while(L > q[i].l) add(--L) ;
        while(R < q[i].r) add(++R) ;
        while(R > q[i].r) del(R--) ; // 以上与普通莫队相同 
        while(T < q[i].ti) addt(++T) ;
        while(T > q[i].ti) addt(T--) ; // 为什么都是add操作呢?看看addt函数就知道了qwq 
        res[q[i].id] = ans ;
    }
    for(int i=1;i<=qcnt;i++) {
        printf("%d\n",res[i]) ;
    }
    return 0 ;
}

 

 PS:scanf读单字符时不忽略空格与换行符T_T,换用cin即可.

转载于:https://www.cnblogs.com/zub23333/p/8545295.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值