[国家集训队]数颜色 / 维护队列(莫队)

嘟嘟嘟


带修改莫队。
以前学过没懂,今天又学了一遍竟然会了。


其实他跟普通莫队相比,就多了一维:时间。
首先也是都离线。
然后对于每一个询问,记录这几个信息:
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;
}
posted @ 2018-12-06 15:50  mrclr  阅读(218)  评论(0编辑  收藏  举报