blasphemy - 题解
题意:有一个数列,支持两种操作。
- 在数列一个数后插入一个新数;
- 选一个区间,每次将区间所有数的值都减 $ 1 $ ,有一个值变为 $ 0 $ 后重复操作,询问会操作几次。询问对接下来的操作不干涉。
题解:
算法一:
询问可转化为一段区间没出现的数最小是多少。每次 $ O(n) $ 暴力即可。
算法二:
没有强制在线就是舒服。
将所有操作离线,预处理出操作在数列中的位置,所有插入操作变为修改操作。这个过程可以用 $ Splay $ 维护。注意数列中元素初始为无穷大。
我们发现信息很难有树形结构维护,于是考虑莫队。莫队维护一段区间每个值出现次数和是否出现,然后我们可以用各种方法维护(树状数组+前缀和,线段树),时间复杂度 $ O(n^{\frac{5}{3}} \log {n}) $。
当然我们可能发现 $ TLE $ 。
优化1:读入优化,输出优化,不必多说。
优化2:我们维护的数据结构不够优秀,莫队的每次修改(一共 $ n^{\frac{5}{3}} $ 次)都需要 $ O(\log n) $ 的时间。我们发现询问操作很少(只有 $ n $ 次),于是我们可以用 $ O(1) $ 修改、 $ O(\sqrt {n}) $ 查询的分块维护,时间复杂度 $ O(n^{\frac{5}{3}} + n \sqrt{n}) $。
优化3:O2优化。
#include<bits/stdc++.h>
#pragma GCC optimize(3)
using namespace std;
const int N=100050,INF=100002;
int n,blo,a[N],ans[N],l=1,r=0,now=0;
int bl[N],L[N],R[N],b[400],cnt[N],blosize;
char pr[N*10],lpr=0;
namespace Splay {
#define lc ch[u][0]
#define rc ch[u][1]
int fa[N],ch[N][2],sz[N],rt=0,tot=0;
void pushup(int u) { sz[u]=1+sz[lc]+sz[rc]; }
void rotate(int u) {
int y=fa[u],z=fa[y],k=ch[y][1]==u,w=ch[u][k^1];
if(z) ch[z][ch[z][1]==y]=u; ch[y][k]=w,ch[u][k^1]=y;
if(w) fa[w]=y; fa[y]=u,fa[u]=z;
pushup(y),pushup(u);
}
void splay(int u,int goal) {
for(int y,z;fa[u]!=goal;rotate(u)) {
y=fa[u],z=fa[y];
if(z!=goal) rotate((ch[y][0]==u)^(ch[z][0]==y)? u:y);
}
if(!goal) rt=u;
}
int getid(int u,int s) {
if(!u) return 0; if(sz[lc]+1==s) return u;
return sz[lc]>=s? getid(lc,s):getid(rc,s-sz[lc]-1);
}
int getrnk(int u) { splay(u,0);return sz[lc]+1; }
void insert(int &u,int ff,int w) {
if(!u) { u=++tot,sz[u]=1,fa[u]=ff,splay(u,0);return ; }
if(w<=sz[lc]) insert(lc,u,w); else insert(rc,u,w-sz[lc]-1);
}
#undef lc
#undef rc
}
struct ASK { int opt,x,y; };ASK k[N];
struct M { int t,x,val; };M mod[N]; int lm=0;
struct P {
int l,r,id,type;
friend bool operator<(P xx,P yy) {
if(xx.l/blo!=yy.l/blo) return xx.l<yy.l;
if(xx.r/blo!=yy.r/blo) return xx.r<yy.r;
return xx.type<yy.type;
}
};P q[N]; int lq=0;
inline int read() {
register int x=0;register char c=getchar();
while(c<'0'||c>'9') c=getchar();
while(c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
return x;
}
void add(int w) { ++cnt[w]; if(cnt[w]==1) ++b[bl[w]]; }
void del(int w) { --cnt[w]; if(!cnt[w]) --b[bl[w]]; }
void update(int nw) {
if(mod[nw].x<=r&&mod[nw].x>=l) del(a[mod[nw].x]),add(mod[nw].val);
swap(a[mod[nw].x],mod[nw].val);
}
int ask() {
int tans=100001;
for(int i=1;i<=bl[N-50];i++)
if(b[i]<R[i]-L[i]+1) {
for(int j=L[i];j<=R[i];j++) if(!cnt[j]) return j;
}
return tans;
}
int main() {
n=read(),blo=pow(n,0.667),blosize=sqrt(n)+1;
for(register int i=1;i<=n;i++) {
k[i].opt=read(),k[i].x=read(),k[i].y=read();
if(k[i].opt==1)
Splay::insert(Splay::rt,0,k[i].y),k[i].y=Splay::tot;
else
k[i].x=Splay::getid(Splay::rt,k[i].x),
k[i].y=Splay::getid(Splay::rt,k[i].y);
}
for(register int i=1;i<=n;i++) {
if(k[i].opt==1) k[i].y=Splay::getrnk(k[i].y);
else
k[i].x=Splay::getrnk(k[i].x),k[i].y=Splay::getrnk(k[i].y);
if(k[i].opt==1) mod[++lm].t=i,mod[lm].val=k[i].x,mod[lm].x=k[i].y;
else
q[++lq].l=k[i].x,q[lq].r=k[i].y,q[lq].id=lq,q[lq].type=lm;
}
for(register int i=1;i<=N-40;i++) a[i]=INF,bl[i]=(i-1)/blosize+1;
for(register int i=1;i<=bl[N-40];i++) L[i]=(i-1)*blosize+1,R[i]=i*blosize;
R[bl[n]]=n,sort(q+1,q+lq+1);
for(int i=1;i<=lq;i++) {
for(;r<q[i].r;) add(a[++r]); for(;l>q[i].l;) add(a[--l]);
for(;r>q[i].r;) del(a[r--]); for(;l<q[i].l;) del(a[l++]);
while(now<q[i].type) update(++now);
while(now>q[i].type) update(now--);
ans[q[i].id]=ask();
}
for(int i=1;i<=lq;i++) printf("%d\n",ans[i]);
return 0;
}