[国家集训队]数颜色 / 维护队列
壹、题目描述
贰、题解
树套树是垃圾,我们就不考虑树套树了
带修莫队?这是什么?在莫队的基础上增加了修改 \(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\%\) 左右。