[国家集训队]数颜色 / 维护队列(莫队)
嘟嘟嘟
带修改莫队。
以前学过没懂,今天又学了一遍竟然会了。
其实他跟普通莫队相比,就多了一维:时间。
首先也是都离线。
然后对于每一个询问,记录这几个信息:
1.左右端点。
2.询问编号。(为了输出答案)
3.时间编号。
然后把询问排序。这个和无修改莫队一样,第一关键字是左端点所在块,第二关键字是右端点所在块。
对于每一个修改,记录这几个信息:
1.时间编号。
2.修改位置。
3.修改后的值。
然后就是根据询问,左右端点加加减减了。但因为有修改,所以每一次移动端点前先要把时间调到当前询问的时间上。
如果询问时间比当前时间晚,就把这期间的修改全做了,并且如果有的修改在当前询问区间内,就看看能否对答案产生贡献;如果询问时间比当前时间早,那么我们就要“时光倒流”,把这期间改过的点都改回来,并且还要看看对答案有没有贡献。
因此,需要记录每一个修改前该位置的数,好方便改回来。
做完这一步之后,当前的时间就是询问时间了,然后就是正常的莫队操作了。
总而言之,带修改莫队也挺暴力的。
#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<vector>
#include<stack>
#include<queue>
using namespace std;
#define enter puts("")
#define space putchar(' ')
#define Mem(a, x) memset(a, x, sizeof(a))
#define rg register
typedef long long ll;
typedef double db;
const int INF = 0x3f3f3f3f;
const db eps = 1e-8;
const int maxn = 5e4 + 5;
const int maxm = 1e6 + 5;
inline ll read()
{
ll ans = 0;
char ch = getchar(), last = ' ';
while(!isdigit(ch)) last = ch, ch = getchar();
while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar();
if(last == '-') ans = -ans;
return ans;
}
inline void write(ll x)
{
if(x < 0) x = -x, putchar('-');
if(x >= 10) write(x / 10);
putchar(x % 10 + '0');
}
int n, m, a[maxn];
char c[2];
const int S = 464;
#define bel(x) (((x) - 1) / S + 1)
struct Node
{
int id, tim, L, R;
bool operator < (const Node& oth)const
{
if(bel(L) != bel(oth.L)) return L < oth.L;
if(bel(R) != bel(oth.R)) return R < oth.R;
return id < oth.id;
}
}q[maxn];
int l = 1, r = 0, cntQ = 0, cur = 0;
int cntC = 0, tim[maxn], pos[maxn], val[maxn], pre[maxn];
int cnt = 0, tot[maxm], ans[maxn];
void change_add(int cur)
{
if(pos[cur] >= l && pos[cur] <= r) if(!--tot[a[pos[cur]]]) cnt--; //把原来的贡献减去
pre[cur] = a[pos[cur]];
a[pos[cur]] = val[cur];
if(pos[cur] >= l && pos[cur] <= r) if(!tot[a[pos[cur]]]++) cnt++; //加上改完后的贡献
}
void change_del(int cur)
{
if(pos[cur] >= l && pos[cur] <= r) if(!--tot[a[pos[cur]]]) cnt--;
a[pos[cur]] = pre[cur];
if(pos[cur] >= l && pos[cur] <= r) if(!tot[a[pos[cur]]]++) cnt++;
}
void change(int now) //把时间调到当前询问上
{
while(cur < cntC && tim[cur + 1] <= now) change_add(++cur);
while(cur && tim[cur] > now) change_del(cur--);
}
void add(int now)
{
if(!tot[a[now]]++) cnt++;
}
void del(int now)
{
if(!--tot[a[now]]) cnt--;
}
int main()
{
n = read(); m = read();
for(int i = 1; i <= n; ++i) a[i] = read();
for(int i = 1; i <= m; ++i)
{
scanf("%s", c);
if(c[0] == 'Q')
{
cntQ++;
q[cntQ].id = cntQ; q[cntQ].tim = i;
q[cntQ].L = read(); q[cntQ].R = read();
}
else tim[++cntC] = i, pos[cntC] = read(), val[cntC] = read();
}
sort(q + 1, q + cntQ + 1);
for(int i = 1; i <= cntQ; ++i)
{
change(q[i].tim);
while(l > q[i].L) add(--l);
while(l < q[i].L) del(l++);
while(r < q[i].R) add(++r);
while(r > q[i].R) del(r--);
ans[q[i].id] = cnt;
}
for(int i = 1; i <= cntQ; ++i) write(ans[i]), enter;
return 0;
}