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

壹、题目描述

传送门 to LUOGU

贰、题解

树套树是垃圾,我们就不考虑树套树了

带修莫队?这是什么?在莫队的基础上增加了修改 \(k\) 一维,表示在执行这个询问前进行了多少次修改,排序还是一样排,即 \(l\) 在同一个块,则按 \(r\) 排,如果 \(r\) 在同一个块,则按照 \(k\) 排序,至于块大小,设为 \(n^{2\over 3}\),证明去这里.

至于移动端点,对于 \(l,r\) 还是十分相同,但是对于 \(k\),我们要考虑我们撤回或者加上的这个修改操作,是否在我们当前维护的区间 \([l,r]\) 中,如果是,则需要多考虑一下 加上/去掉 对于维护的值的影响,在考虑之后,在原序列中 加上/去掉 这个修改。

赶脚还是挺好理解的吧?

叁、代码

#include<cstdio>
#include<algorithm>
using namespace std;

typedef long long ll;
template<class T>inline T readin(T x){
	x=0; int f=0; char c;
	while((c=getchar())<'0' || '9'<c) if(c=='-') f=1;
	for(x=(c^48); '0'<=(c=getchar()) && c<='9'; x=(x<<1)+(x<<3)+(c^48));
	return f? -x: x;
}

const int maxn=133333;
const int maxm=133333;
const int maxc=1e6;
const int block=2700; // pow(n, 2/3)

inline int getb(int x){return (x-1)/block;}
struct query{
	int l, r, k, id;
	query(){}
	query(int L, int R, int K, int I): l(L), r(R), k(K), id(I){}
	inline int operator <(query rhs){
		if(getb(l)!=getb(rhs.l)) return l<rhs.l;
		if(getb(r)!=getb(rhs.r)) return (getb(l)&1)? r<rhs.r: r>rhs.r;
		return k<rhs.k;
	}
}q[maxm+5];
int qcnt;

struct command{
	// pre: the color before change
	int pos, x, pre;
	command(){}
	command(int P, int X, int R): pos(P), x(X), pre(R){}
}cmd[maxm+5];
int ccnt;

int n, m;

int a[maxn+5], b[maxn+5];
// a: the initial array
// b: modify array

inline void input(){
	n=readin(1), m=readin(1);
	for(int i=1; i<=n; ++i)
		a[i]=b[i]=readin(1);
	char opt[5]; int x, y, cnt=0;
	for(int i=1; i<=m; ++i){
		scanf("%s %d %d", opt, &x, &y);
		if(opt[0]=='R'){
			cmd[++ccnt]=command(x, y, b[x]);
			b[x]=y, ++cnt;
		}
		else{
			++qcnt;
			q[qcnt]=query(x, y, cnt, qcnt);
		}
	}
}

int ans[maxm+5], cnt[maxc+5];
inline void getans(){
	// save the current info
	int l=1, r=0, k=0, now=0;
	sort(q+1, q+qcnt+1);
	for(int i=1; i<=qcnt; ++i){
		while(k<q[i].k){
			++k;
			if(l<=cmd[k].pos && cmd[k].pos<=r){
				if(!--cnt[a[cmd[k].pos]]) --now;
				if(!cnt[cmd[k].x]++) ++now;
			}
			a[cmd[k].pos]=cmd[k].x;
		}
		while(k>q[i].k){
			if(l<=cmd[k].pos && cmd[k].pos<=r){
				if(!--cnt[a[cmd[k].pos]]) --now;
				if(!cnt[cmd[k].pre]++) ++now;
			}
			a[cmd[k].pos]=cmd[k].pre, --k;
		}
		while(l<q[i].l) if(!--cnt[a[l++]]) --now;
		while(l>q[i].l) if(!cnt[a[--l]]++) ++now;
		while(r<q[i].r) if(!cnt[a[++r]]++) ++now;
		while(r>q[i].r) if(!--cnt[a[r--]]) --now;
		ans[q[i].id]=now;
	}
	for(int i=1; i<=qcnt; ++i)
		printf("%d\n", ans[i]);
	putchar('\n');
}

signed main(){
	input();
	getans();
	return 0;
}

肆、用到の小 \(\tt trick\)

带修莫队,无了。

那就写一下使用莫队的条件吧:

  • 询问之间必须互不影响,是独立的;
  • 询问必须是一个区间,不能有别的因子,也就是必须形如 \(l,r\)
  • 要能够离线;

带修莫队还得有个更大的前提:

  • 操作支持撤回;

同时写一下莫队的一个小 \(\tt trick\):奇偶化排序

奇偶化排序即对于属于奇数块的询问,\(r\) 按从小到大排序,对于属于偶数块的排序,\(r\) 从大到小排序。

在一般莫队的情况下,效率提升 \(30\%\) 左右。

posted @ 2021-03-25 15:54  Arextre  阅读(51)  评论(0编辑  收藏  举报