题解-Bombs

题解-Bombs

前置知识:

线段树


(color{orange}{ exttt{Bombs on luogu}}) / (color{orange}{ exttt{Bombs on CF}})

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

数据范围:(1le nle 3 imes 10^5)


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

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

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

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

而将当前序列值 (p_i) 退让成 (p_k=p_i-1) 则相当于树立 (1sim 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,"
 "[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,"
 "[i<n-1]); //未被删除的序列值
	}
	return 0;
}

祝大家学习愉快!

原文地址:https://www.cnblogs.com/George1123/p/12558365.html