P9247 [集训队互测 2018] 完美的队列 题解
小清晰分块,不卡常\w/。
首先区间push操作时困难的,考虑求出所有操作所有的元素到什么时候全部弹出,求出这个东西后很好统计答案。
考虑分块,散块直接暴力做。整块考虑双指针,等价于有若干元素支持整体加减 \(1\),单点加减 \(1\),统计实时小于等于 \(0\) 的元素个数,这个直接开 \(n\) 个链表就做完啦(感觉这个trick很有前途啊,正在考虑能不能用它出道分块题)。时间空间复杂度都是 \(O(n\sqrt{n})\) 的。代码可能细节比较多,我的实现很丑/kk,建议不看。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define N 100005
#define B 450
#define CB (N/B+5)
#define pii pair<int,int>
#define fi first
#define se second
int read(){
int x=0;
char ch=getchar();
while(ch<48)ch=getchar();
while(ch>47)x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
return x;
}
int n,m,r,a[N],nxt[N],ans[N];
int L[CB],R[CB],bl[N];
vector<int>col[N];
struct query{
int l,r,x;
void init(){
l=read(),r=read(),x=read();
}
}q[N];
struct queeu{
int L[N<<2],R[N<<2],len[N<<2],bl[N<<2],cnt,rp;
void clear(){
cnt=rp=0;
for(int i=0;i<=r;i++)
len[i]=0,L[i]=R[i]=i;
for(int i=r+1;i<=r+max(n,m);i++)
L[i]=R[i]=len[i]=0,bl[i]=1;
}
int getr(int dep){
return (dep%r+r)%r;
}
void remove(int x){
x+=r;
R[L[x]]=R[x],L[R[x]]=L[x],len[getr(bl[x])]--;
}
void insert(int dep,int x){
x+=r;
if(bl[x]+rp>0&&dep<=0)cnt++;
if(bl[x]+rp<=0&&dep>0)cnt--;
bl[x]=dep-rp,dep=getr(dep-rp),len[dep]++;
R[L[dep]]=x,L[x]=L[dep],R[x]=dep,L[dep]=x;
}
void addall(){
cnt-=len[getr(0-rp)],rp++;
}
void suball(){
cnt+=len[getr(1-rp)],rp--;
}
}li;
int qryidx(int pos){
return li.bl[pos+r]+li.rp;
}
void calcB(int o,int i,int f){
if(q[i].l<=L[o]&&R[o]<=q[i].r)
f<0?li.suball():li.addall();
else
for(int j=max(q[i].l,L[o]);j<=min(q[i].r,R[o]);j++){
int now=li.bl[j+r]+li.rp;
li.remove(j),li.insert(now+f,j);
}
}
queue<pii>Q[N];
vector<int>V;
int sz[N],now[N],all;
void tpush(int o,int x){
Q[o].push({x,all-now[o]}),sz[o]+=all-now[o]+1;
while(sz[o]>a[o]){
pii cur=Q[o].front();
if(cur.se){
int minn=min(sz[o]-a[o],cur.se);
Q[o].front().se-=minn,sz[o]-=minn;
}
else{
nxt[cur.fi]=max(nxt[cur.fi],sz[o]==a[o]+1?x:V[now[o]+1+a[o]-(sz[o]-(all-now[o]+1))] );
Q[o].pop(),sz[o]--;
}
}
now[o]=all;
}
void work1(int o,int len){
li.clear();
for(int i=L[o];i<=R[o];i++)
li.insert(a[i],i);
int nx=0;
while(nx<m&&li.cnt<len)
calcB(o,++nx,-1);
for(int i=1;i<=m;i++){
if(q[i].l>R[o]||L[o]>q[i].r)
continue;
calcB(o,i,1);
while(nx<m&&li.cnt<len)
calcB(o,++nx,-1);
if(q[i].l<=L[o]&&R[o]<=q[i].r)
nxt[i]=max(nxt[i],nx+(li.cnt<len));
}
}
void work2(int o){
all=0,V.clear(),V.push_back(0);
for(int i=1;i<=m;i++){
if(q[i].l<L[o]&&R[o]<q[i].r)
all++,V.push_back(i);
if(bl[q[i].l]!=o&&bl[q[i].r]!=o)
continue;
for(int j=max(q[i].l,L[o]);j<=min(q[i].r,R[o]);j++)
tpush(j,i);
}
for(int i=L[o];i<=R[o];i++)
tpush(i,m+1);
for(int i=L[o];i<=R[o];i++)
while(Q[i].size())
nxt[Q[i].front().fi]=m+1,Q[i].pop();
}
int main(){
n=read(),m=read();
for(int i=1;i<=n;i++){
r=max(r,a[i]=read());
bl[i]=bl[i-1];
if(i%B==1)R[bl[i]]=i-1,L[++bl[i]]=i;
}
R[bl[n]]=n,r+=m;
for(int i=1;i<=m;i++)
q[i].init(),nxt[i]=i+1,col[q[i].x].push_back(i);
for(int i=1;i<=bl[n];i++)
work1(i,R[i]-L[i]+1),work2(i);
nxt[m+1]=m+1;
for(int i=1;i<=100000;i++){
int l=1,r=1;
for(auto &&j:col[i]){
if(r>=j)r=max(r,nxt[j]);
else ans[l]++,ans[r]--,l=j,r=nxt[j];
}
ans[l]++,ans[r]--;
}
for(int i=1;i<=m;i++)
printf("%d\n",ans[i]+=ans[i-1]);
return 0;
}