题解-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) 后的序列值变化。
- 如果 (q_j<i):序列值不改变。
- 如果 (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;
}
祝大家学习愉快!