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