題解 CF1326E Bombs


CF1326E Bombs

題目大意

有一個長度為 \(n\) 的排列 \(p_{1}, p_{2},\dots, p_{n}\)。其中一些位置上是有炸彈的(保證至少一個位置沒有炸彈)。

對於一個固定的局面(已知哪些位置有炸彈),我們這樣定義它的權值:考慮如下過程:

維護一個集合 \(A\),初始時為空。從 \(1\)\(n\) 遍歷所有 \(i\)

  • \(p_{i}\) 加入集合 \(A\)
  • 如果 \(i\) 位置上有炸彈,則刪除 \(A\) 中最大的數。

顯然最后 \(A\) 里至少會剩一個數。該局面的權值就是此時 \(A\) 里最大的數。

初始時沒有炸彈。接下來 \(n - 1\) 個時刻,每個時刻會在一個原本沒有炸彈的位置上放一顆炸彈。告訴你這些位置,請你求出每個時刻局面的權值。

數據范圍:\(2\leq n\leq 300\, 000\)

本題題解

考場上的一點分析:依次加入炸彈的過程中,每個炸彈會匹配到原序列的一個值——也就是它要炸掉的值。但是因為炸彈不是按位置順序加入的,所以在加入的過程中,炸彈和值的匹配關系會發生變化,而這個變化會產生連鎖反應,不好快速維護。(例如樣例二中就可以看出這種匹配關系的變化)


正解

換個角度思考。容易發現答案是單調不增的。所以我們可以把問題轉化為,判斷當前答案是否小於某個值\(x\)

也就是說,我們需要炸掉所有\(\geq x\)的值。

  • 考慮最右邊的\(\geq x\)的值,它右邊必須至少有一個炸彈。
  • 右起第二個\(\geq x\)的值,它右邊必須至少有兩個炸彈。
  • ......
  • 右起第\(k\)\(\geq x\)的值,它右邊必須至少有\(k\)個炸彈。

考慮構造一個序列\(b_i\),表示\(i\dots n\)\(\geq x\)的值的數量 減去 炸彈的數量。

發現答案小於\(x\)當且僅當所有\(b_i\)\(\leq0\)。也就是說每個\(\geq x\)的值,都能被至少一個炸彈“照顧”到。

用線段樹維護\(b\)序列,支持區間加、求全局最大值即可。

時間復雜度\(O(n\log n)\)

總結

發現了答案的單調性后,我們把問題轉化為:判斷當前答案是否小於某個值\(x\)。這有點像二分答案,這樣做的好處是:所有需要被炸掉的值都是確定的了。我們只要判斷能否把它們都炸掉即可。

參考代碼

//problem:CF1326E
#include <bits/stdc++.h>
using namespace std;

#define pb push_back
#define mk make_pair
#define lob lower_bound
#define upb upper_bound
#define fst first
#define scd second

typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;

namespace Fread{
const int MAXN=1<<20;
char buf[MAXN],*S,*T;
inline char getchar(){
	if(S==T){
		T=(S=buf)+fread(buf,1,MAXN,stdin);
		if(S==T)return EOF;
	}
	return *S++;
}
}//namespace Fread
#ifdef ONLINE_JUDGE
	#define getchar Fread::getchar
#endif
inline int read(){
	int f=1,x=0;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
inline ll readll(){
	ll f=1,x=0;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
/*  ------  by:duyi  ------  */ // myt天下第一
const int MAXN=3e5;
int n,a[MAXN+5],q[MAXN+5],pos[MAXN+5];
struct SegmentTree{
	int val[MAXN*4+5],tag[MAXN*4+5];
	void push_down(int p){
		if(tag[p]){
			val[p<<1]+=tag[p];
			tag[p<<1]+=tag[p];
			val[p<<1|1]+=tag[p];
			tag[p<<1|1]+=tag[p];
			tag[p]=0;
		}
	}
	void push_up(int p){
		val[p]=max(val[p<<1],val[p<<1|1]);
	}
	void modify(int p,int l,int r,int ql,int qr,int v){
		if(ql<=l && qr>=r){
			val[p]+=v;
			tag[p]+=v;
			return;
		}
		push_down(p);
		int mid=(l+r)>>1;
		if(ql<=mid)modify(p<<1,l,mid,ql,qr,v);
		if(qr>mid)modify(p<<1|1,mid+1,r,ql,qr,v);
		push_up(p);
	}
	SegmentTree(){}
}T;
int main() {
	n=read();
	for(int i=1;i<=n;++i)a[i]=read(),pos[a[i]]=i;
	for(int i=1;i<=n;++i)q[i]=read();
	int ans=n;
	T.modify(1,1,n,1,pos[n],1);
	for(int i=1;i<=n;++i){
		printf("%d ",ans);
		T.modify(1,1,n,1,q[i],-1);
		if(i==n)break;
		while(1){
			assert(ans>=1);
			if(T.val[1]<=0){
				--ans;
				T.modify(1,1,n,1,pos[ans],1);
			}
			else break;
		}
	}
	puts("");
	return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM