LOJ#2461. 「2018 集訓隊互測 Day 1」完美的隊列 (分塊+離線+two_pointers)

題目描述: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);
	}
}

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章