题目描述:https://loj.ac/problem/2461

 

 

题解

非常巧妙的一道题

我们对于一个操作i:[l,r,v],求出这个操作中最后一个被pop掉的v的时间(记为ed[i])

即经历了(i,ed[i]]这些操作后,操作i的所有push进来的元素都被pop掉了

如果我们可以求出所有操作的ed,我们就可以简单求出每一时刻的答案(自行思考,存一下每个元素的出现次数即可)

 

如何求ed[i]?

首先,我们知道,一个元素push进了某一个队列k,如果这个队列x再被后来的操作覆盖a[k]次,这个元素就会被pop出去了

(这个和queue是否为空无关,因为它只会在溢出的时候push)

那么有以下式子

ed[i]=max_{k=l_i}^{r_i}last(i,k)

last(i,k)表示位置k在第i个操作push进来的元素会在第last_k个操作时pop掉

换一种枚举方式,我们可以枚举每一个位置对于所有操作的ed的贡献

于是我们就有了一个O(nm)的暴力:

对每一个位置进行一下two_pointers,我们可以求出当前位置k对于所有的i的last(i,k)值

具体怎么求?

首先我们知道位置k要被覆盖a[k]次

设l指针为i,r指针为last(i,k)

当l++时,如果操作l-1覆盖了位置k,那么位置k的剩余覆盖次数++

当r++时,如果操作r覆盖了位置k,那么位置k的剩余覆盖次数--

移动一下左端点,就需要一直移动右端点,直到k的剩余覆盖次数=0,此时的j就是last(i,k),再移动一下左端点求下一个i的答案

然后把每求出一个last(i,k)更新到ed[i]=max(ed,last(i,k))中

即可求出所有操作的ed值

 

考虑一下怎么来优化一下这个暴力

理性分析一下,我们之所以能够用two_pointers来求出每一个操作的last(i,k)值,是因为位置k的a[k]是不变的

如果我们进行一下分块。。。

则每一个块中的a的组成都是不变的,这样的话求出来的last与i也是正相关的,即也可以运用two_pointers

那么有以下式子

ed[i]=max([li,ri]覆盖的整块的最大last值,[li,ri]覆盖的零散块的最大last值)

也可以换一种枚举方式,求每一个整块和零散块对所有操作的贡献

先来考虑最简单的情况

1、整块

显然,一个整块想要被pop完,只与当前整块中的最大的剩余覆盖次数有关

所以先把mx初始化为当前块中的最大a值

注意two_pointers在计算last(i)是要先删掉操作i的贡献

当two_pointers在新加入/删除一个操作的时候又要分两种情况:

(1)、完全覆盖当前块

存一个cov标记,表示当前的整块被覆盖了多少次

在移动r指针时判断一下mx-cov是否为0即可

(2)、与当前块有交集

直接暴力修改当前块的a值,在重新计算mx即可(注意,这里不能修改真正的a值,所有要先把a值转存一下)

 

2、零散块

如果我们直接对每个点two_pointers的话就会把时间复杂度退化为O(nm)(甚至更高)

所以我们要利用之前整块进行two_pointers的信息来进行优化

我们发现之前的整块加操作会对当前块中的零散块造成相同的影响

所以我们存一下整块加的前缀和sum[i]表示从操作1~操作i中当前块一共经历了多少次完整覆盖

设c[i]表示第i个完全覆盖当前块的操作编号

d[i]表示第i个与当前块相交的操作编号

e[i]表示lower_bound(c+1,c+cn+1,d[i])-c-1

于是我们就可以对每个点进行two_pointers,只不过更新的ed只有编号在d集合中的ed值

(之前的整块相当于只对c集合进行更新,现在的零散块相当于只对d集合进行更新)

细节较多,需要细细考虑

代码:

#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
inline int gi()
{
	char c;int num=0,flg=1;
	while((c=getchar())<'0'||c>'9')if(c=='-')flg=-1;
	while(c>='0'&&c<='9'){num=num*10+c-48;c=getchar();}
	return num*flg;
}
#define N 100005
#define D 360
int n,m,a[N],b[N];
int c[N],d[N],e[N],sum[N],cn,dn;
int vis[N],num,ed[N];
int l[N],r[N],v[N];
struct node{int x,y;};
vector<node>g[N];
int main()
{
	int i,j,k,mx,cov,L,R;
	n=gi();m=gi();
	for(i=1;i<=n;i++)a[i]=gi();
	for(i=1;i<=m;i++)l[i]=gi(),r[i]=gi(),v[i]=gi();
	for(L=1;L<=n;L+=D){ 
		cn=dn=mx=cov=0;
		R=min(n,L+D-1);
		for(i=L;i<=R;i++)mx=max(mx,b[i]=a[i]);
		for(i=1,j=0;i<=m;i++){
			if(l[i]<=L&&r[i]>=R)cov--;
			else if(l[i]<=R&&r[i]>=L){
				for(k=max(l[i],L);k<=min(r[i],R);k++)b[k]++;
				mx=0;for(k=L;k<=R;k++)mx=max(mx,b[k]);
			}
			while(j<=m&&mx-cov>0){
				j++;
				if(l[j]<=L&&r[j]>=R)cov++;
				else if(l[j]<=R&&r[j]>=L){
					for(k=max(l[j],L);k<=min(r[j],R);k++)b[k]--;
					mx=0;for(k=L;k<=R;k++)mx=max(mx,b[k]);
				}
			}
			sum[i]=sum[i-1];
				if(l[i]<=L&&r[i]>=R){
					ed[i]=max(ed[i],j);
					c[++cn]=i;sum[i]++;
				}
				else if(l[i]<=R&&r[i]>=L)d[++dn]=i,e[dn]=cn;
			}
		for(i=L;i<=R;i++){
			mx=a[i];
			for(j=1,k=0;j<=dn;j++){
				mx+=sum[d[j]]-sum[d[j-1]];
				if(l[d[j]]<=i&&r[d[j]]>=i){
					mx++;    
					while(k<dn&&mx>0){
						k++;
						mx-=sum[d[k]]-sum[d[k-1]];
						if(l[d[k]]<=i&&r[d[k]]>=i)
							mx--;
					}
					if(mx>0){
						if(sum[m]-sum[d[k]]<mx)ed[d[j]]=max(ed[d[j]],m+1);
						else ed[d[j]]=max(ed[d[j]],c[e[k]+mx]);
						continue;
					}
					if(l[d[k]]<=i&&r[d[k]]>=i){
						if(mx<0)ed[d[j]]=max(ed[d[j]],c[e[k]+mx+1]);
						else ed[d[j]]=max(ed[d[j]],d[k]);
					}
					else ed[d[j]]=max(ed[d[j]],c[e[k]+mx]);
				}
			}
		}
	}
	for(i=1;i<=m;i++){
		g[i].push_back((node){v[i],1});
		g[ed[i]].push_back((node){v[i],-1});
	}
	for(i=1;i<=m;i++){
		for(j=0;j<(int)g[i].size();j++){
			int x=g[i][j].x,y=g[i][j].y;
			if((vis[x]+y==0)^(vis[x]==0))num+=y;
			vis[x]+=y;
		}
		printf("%d\n",num);
	}
}