题解-Bombs

题解-Bombs

前置知识:

线段树


\(\color{orange}{\texttt{Bombs on luogu}}\) / \(\color{orange}{\texttt{Bombs on CF}}\)

有一个 \(n\) 的排列 \(p_i\)。其中有一些 \(i\) 被打了标记。对于 \(i=1\to n\),将 \(p_i\) 放入集合,如果 \(i\)标记,就删除集合中的最大数。最后剩下的集合的最大数就是这种标记状态下该序列的值。给定另一个 \(n\) 的排列 \(q_j\),求对于 \(j=1\to n\),将 \(q_1\sim q_j\) 标记后对应的序列值

数据范围:\(1\le n\le 3\times 10^5\)


考虑当前序列值为 \(p_i\),如果打上标记 \(q_j\) 后的序列值变化。

  1. 如果 \(q_j<i\):序列值不改变。
  2. 如果 \(q_j\ge i\):序列值变小

所以打上标记 \(q_j\) 相当于破坏 \(1\sim q_j\) 的最大值

可以把打标记 \(q_j\) 变化为令 \([1,q_j]\)\(w\) 区间值 \(-1\)

而将当前序列值 \(p_i\) 退让成 \(p_k=p_i-1\) 则相当于树立 \(1\sim k\) 的最大值

可以把退让序列值变化为令 \([1,k]\)\(w\) 区间值 \(+1\)

这样的话如果有 \(w_i>0\) 就说明当前序列值未被删除,否则就该退让最大值


因为要看是否满足有 \(w_i>0\),所以可以线段树维护 \(w\) 区间最大值

Code

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

//&Start
#define re register
#define il inline
#define inf 0x3f3f3f3f
typedef long long lng;

//&Data
#define N 300000
int n,p[N+10],q[N+10],I[N+10];

//&Segmenttree
struct smt{ //线段树
	int mx[N<<2|7],mk[N<<2|7];
	il void pushdown(re int k){
		if(mk[k]!=0) mx[k<<1]+=mk[k],mx[k<<1|1]+=mk[k],mk[k<<1]+=mk[k],mk[k<<1|1]+=mk[k],mk[k]=0;
	}
	il void add(re int x,re int y,re int z,re int k=1,re int l=1,re int r=n){
		if(x<=l&&r<=y){mx[k]+=z,mk[k]+=z;return;}
		int mid=(l+r)>>1; pushdown(k);
		if(mid>=x) add(x,y,z,k<<1,l,mid);if(mid<y) add(x,y,z,k<<1|1,mid+1,r);
		mx[k]=max(mx[k<<1],mx[k<<1|1]);
	}
}tree;

//&Main
int main(){
	scanf("%d",&n);
	for(re int i=1;i<=n;i++) scanf("%d",p+i),I[p[i]]=i; //记录pk对应的k,方便退让
	for(re int i=1;i<=n;i++) scanf("%d",q+i);
	re int res=n;
	tree.add(1,I[res],1),printf("%d%c",res,"\n "[1<n]);
	for(re int i=1;i<n;i++){
		tree.add(1,q[i],-1); //打标记-删除
		while(tree.mx[1]<=0) tree.add(1,I[--res],1);//退让知道未被删除
		printf("%d%c",res,"\n "[i<n-1]); //未被删除的序列值
	}
	return 0;
}

祝大家学习愉快!

猜你喜欢

转载自www.cnblogs.com/Wendigo/p/12558365.html
今日推荐