# 【洛谷】P1903 [国家集训队]数颜色 / 维护队列 带修莫队

1、 Q L R代表询问你从第L支画笔到第R支画笔中共有几种不同颜色的画笔。

2、 R P Col 把第P支画笔替换为颜色Col。

 题意：如题

## 思路：

AC代码：

#include<iostream>
#include<string>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include <queue>
#include<sstream>
#include <stack>
#include <set>
#include <bitset>
#include<vector>
#define FAST ios::sync_with_stdio(false)
#define abs(a) ((a)>=0?(a):-(a))
#define sz(x) ((int)(x).size())
#define all(x) (x).begin(),(x).end()
#define mem(a,b) memset(a,b,sizeof(a))
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define rep(i,a,n) for(int i=a;i<=n;++i)
#define per(i,n,a) for(int i=n;i>=a;--i)
#define pb push_back
#define mp make_pair
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef pair<ll,ll> PII;
const int maxn = 2e6+200;
const int inf=0x3f3f3f3f;
const double eps = 1e-7;
const double pi=acos(-1.0);
const int mod = 1e9+7;
inline int lowbit(int x){return x&(-x);}
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
void ex_gcd(ll a,ll b,ll &d,ll &x,ll &y){if(!b){d=a,x=1,y=0;}else{ex_gcd(b,a%b,d,y,x);y-=x*(a/b);}}//x=(x%(b/d)+(b/d))%(b/d);
inline ll qpow(ll a,ll b,ll MOD=mod){ll res=1;a%=MOD;while(b>0){if(b&1)res=res*a%MOD;a=a*a%MOD;b>>=1;}return res;}
inline ll inv(ll x,ll p){return qpow(x,p-2,p);}
inline ll Jos(ll n,ll k,ll s=1){ll res=0;rep(i,1,n+1) res=(res+k)%i;return (res+s)%n;}
inline int read(){ int f = 1; int x = 0;char ch = getchar();while(ch>'9'||ch<'0') {if(ch=='-') f=-1; ch = getchar();}while(ch>='0'&&ch<='9') x = (x<<3) + (x<<1) + ch - '0',  ch = getchar();return x*f; }
int dir[4][2] = { {1,0}, {-1,0},{0,1},{0,-1} };

int a[maxn];
int pos[maxn];
int cnt[maxn];
int Ans[maxn];
int n, m;
int sum = 0;

typedef struct Time     //记录每个时间截的修改
{
int p;
int col;
}T;
T c[maxn];

typedef struct Query    //记录查询
{
int l;
int r;
int t;
int id;
}Q;
Q q[maxn];

bool cmp(Q a , Q b)     //三个键的比较
{
if(pos[a.l]!=pos[b.l]) return pos[a.l]<pos[b.l];
if(pos[a.r]!=pos[b.r])return ( (pos[a.l] &1 )?pos[a.r] < pos[b.r] : pos[a.r] > pos[b.r] );
return a.t<b.t;
}

{
if(!cnt[x]) ++sum;
++cnt[x] ;
}

inline void del(int x)      //这两个函数和普通莫队一样
{
--cnt[x] ;
if(!cnt[x]) --sum;
}

inline void modify(int x, int ti)         //对于当前时间截的修改
{
if(c[ti].p >= q[x].l && c[ti].p <= q[x].r) del(a[c[ti].p]), add(c[ti].col);     //如果当前时刻要修改的位置在本次区间中，那么就去掉原先的颜色，添加进要改的颜色
swap(a[c[ti].p], c[ti].col);        //两者交换一下，因为下一次回到这个时刻肯定又换回来了。
}

int main()
{
int block = pow(n,2/3.0);
rep(i,1,n) a[i] = read(), pos[i] = (i-1)/block + 1;
int p1 = 0, p2 = 0;     //p1记录查询个数， p2记录修改个数
rep(i,1,m)
{
char s[5]; scanf("%s",s);
if(s[0] == 'Q') q[++p1].l = read(), q[p1].r = read(), q[p1].t = p2, q[p1].id = p1;      //同时记录当前时间截
}
sort(q+1,q+1+p1,cmp);       //排序
int now = 0; int L = 0, R = 0;
rep(i,1,p1)
{
while(q[i].l > L) del(a[L++]);
while(q[i].r < R) del(a[R--]);      //上面四个和普通莫队一样
while(now < q[i].t) modify(i,++now);        //增加到当前时间截，从下一个时刻开始修改
while(now > q[i].t) modify(i,now--);        //回溯到当前时间截，从当前开始一个个修改回去
Ans[q[i].id] = sum;
}
rep(i,1,p1) printf("%d\n",Ans[i]);
return 0;
}


03-04 182

09-22