LOJ2461. 「2018 集训队互测 Day 1」完美的队列
有\(n\)个队列,每个队列上限是\(a_i\)。如果队列超过上限就弹队头。
每次给\([l,r]\)的队列加入颜色\(x\)。
问所有时间中,存在于至少一个队列的颜色的个数。
\(n\le 10^5\)
好题。
离线。现在考虑求出一个操作的存活时间区间。
分块,把操作拆成整块操作和散块操作,分别计算其存活时间并取max。
显然,如果两种操作操作区间相同,那么前者比后者死得早。根据这个单调性搞搞事情:
求整块操作的存活时间:
如果固定一个整块操作,在刚操作时令\(h_i=a_i\);后面每次别的操作(整块或散块)操作能使得一些\(h_i\)减一。如果\(\max(h_i)\le 0\),则这个整块操作刚好死亡。
根据单调性,在这个整块操作死去之后,计算下一个整块操作前先继承一些信息。显然要\(h_i\)加上两者间对队列\(i\)的操作次数。如果两者间存在散块操作,就可以扫整个块暴力更新,因为复杂度可以摊到其中任意一个散块上;如果不存在散块操作,就打个区间加一的标记。
求散块操作的存活时间:
对于散点分别维护个队列,队列中每个元素除了记操作编号之外,还有它被加入时整块操作加入过多少个。于是就可以得知两个散块操作之间,一共进行了多少次操作。最后用个双指针扫过去就可以得到每个散块操作的存活时间。
时间\(O(n\sqrt n)\)。
并不是很好实现,整块和散块之间的各类影响错综复杂,写的时候要好好整理一下。
精细实现2h左右,几乎没有调试,交上去没有WA,倒是一车的MLE。经过重重卡空间终于过去了……
using namespace std;
#include <bits/stdc++.h>
#define fi first
#define se second
#define mp make_pair
const int N=100005,B=400,INF=1000000000;
int n,m;
int a[N],c[N];
struct Itv{int l,r;} p[N];
int mx[N];
vector<int> o[N];
void upd(int t,int r){mx[t]=max(mx[t],r);}
int buc[N];
int bel[N],R[N],nb;
struct info{int t,z;};
int sz,firb,hp[B+5],cd;
vector<info> qb;
vector<int> qd[B+5];
int zd[N];
int h[B+5],mxh,tag;
void reset_h(int lst){
mxh=-INF;
for (int i=1;i<=sz;++i){
while (hp[i]<qd[i].size() && zd[qd[i][hp[i]]]==lst)
hp[i]++,h[i]++;
mxh=max(mxh,h[i]+tag);
}
}
void adjust(int t){
if (firb<qb.size() && mxh<=0){
info f=qb[firb++];
upd(f.t,t);
if (firb<qb.size()){
info g=qb[firb];
if (g.z)
reset_h(firb);
mxh++,tag++;
}
}
}
void mdf_d(int st,int en,int t){
if (st>en) return;
cd++;
zd[t]=qb.size();
for (int i=st;i<=en;++i){
h[i]--;
qd[i].push_back(t);
}
mxh=-INF;
for (int i=1;i<=sz;++i)
mxh=max(mxh,h[i]+tag);
adjust(t);
}
void mdf_b(int t,int os){
mxh--,tag--;
adjust(t);
if (firb==qb.size()){
mxh=-INF;
for (int i=1;i<=sz;++i){
mxh=max(mxh,h[i]=a[os+i]);
hp[i]=qd[i].size();
}
tag=0;
}
qb.push_back({t,cd});
cd=0;
}
void workd(int d,int ad){
for (int i=0,j=0;i<qd[d].size();++i){
while (j<qd[d].size() && (zd[qd[d][j]]+j)-(zd[qd[d][i]]+i)<=ad)
++j;
int tmp=(zd[qd[d][i]]+i+ad)-(zd[qd[d][j-1]]+j-1);
if (tmp==0)
upd(qd[d][i],qd[d][j-1]);
else{
tmp+=zd[qd[d][j-1]];
upd(qd[d][i],(tmp<=qb.size()?qb[tmp-1].t:m+1));
}
}
}
int main(){
scanf("%d%d",&n,&m);
for (int i=1;i<=n;++i)
scanf("%d",&a[i]);
for (int i=1;i<=n;++i)
bel[i]=(i-1)/B+1,R[bel[i]]=i,nb=bel[i];
for (int i=1;i<=m;++i)
scanf("%d%d%d",&p[i].l,&p[i].r,&c[i]);
for (int i=1;i<=nb;++i){
sz=R[i]-R[i-1];
qb.clear();
for (int j=1;j<=sz;++j)
qd[j].clear(),h[j]=hp[j]=0;
firb=cd=mxh=tag=0;
for (int t=1;t<=m;++t)
if (p[t].l<=R[i-1]+1 && R[i]<=p[t].r)
mdf_b(t,R[i-1]);
else
mdf_d(max(p[t].l,R[i-1]+1)-R[i-1],min(p[t].r,R[i])-R[i-1],t);
for (;firb<qb.size();++firb)
upd(qb[firb].t,m+1);
for (int j=1;j<=sz;++j)
workd(j,a[j+R[i-1]]);
}
for (int i=1;i<=m;++i){
o[i].push_back(c[i]);
o[mx[i]].push_back(-c[i]);
}
int ans=0;
for (int i=1;i<=m;++i){
for (int j=0;j<o[i].size();++j){
int x=abs(o[i][j]),c=(o[i][j]>0?1:-1);
ans-=(buc[x]!=0);
buc[x]+=c;
ans+=(buc[x]!=0);
}
printf("%d\n",ans);
}
return 0;
}