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;
}

posted @ 2021-07-11 22:03  jz_597  阅读(185)  评论(0编辑  收藏  举报