*题解:CF1326E Bombs

题目链接

解析

求答案相当于是求从 \(n\) 开始连续删了多少个数,要求即为在当前删除标记分布情况下, \(\ge x\) 的数是否都被删。先考虑一次询问怎么做,考虑二分。一个直接的想法是对于每个 \(\ge x\) 的数,尝试匹配其右边第一个未被匹配的删除标记,若有未被匹配的 \(\ge x\) 的数则说明 \([x,n]\) 的数没有都被删,答案至少为 \(x\),当前二分左端点 \(l\leftarrow x\)。

现在考虑多个询问,首先发现答案随询问单调不增,用双指针替代二分。然后发现多询问其实相当于有一个添加删除标记的操作,此时匹配状态并不好维护。考虑转化条件,发现我们关心的是是否存在一个 \(\ge x\) 的数没有被匹配,故可以将 \(\ge x\) 的数看作是本质相同。然后来看每个后缀,因为整体不能把所有 \(\ge x\) 的数删掉等价于存在一个后缀不能把后缀中 \(\ge x\) 的数删掉。事实上,对于每个后缀,可以看作是 \(\ge x\) 的数在前,删除标记在后。单拎出来看这是一个更强的条件,但如果对于每个后缀都这么判断,就可以发现这个条件是必要的。具体地,将 \(\ge x\) 的数看作 \(1\),删除操作看作是 \(-1\),判断相当于取后缀和,若后缀和 \(\ge 1\) 则说明不能把所有 \(\ge x\) 的数删掉。问题是对于后缀和 \(\le 0\) 的后缀,也有可能出现删不完的情况。这是因为后缀中有和 \(< 0\) 的前缀做了多余的贡献,把这段前缀去掉就变成了新的后缀,就这样一直删必定可以删成一个后缀和 \(\ge 1\) 的后缀。

故我们关心所有位置后缀和的最大值,若其 \(> 1\) 则说明当前答案 \(x\) 合法,否则令 \(x \leftarrow x - 1\),更新后缀和,体现为后缀加。添加删除标记时同样更新后缀和,体现为前缀减。线段树维护即可。

时间复杂度 \(O(n\log n)\)。

没想到的地方

考虑每个后缀,转化条件。

总结

条件不好刻画时尝试转化成看起来更强 / 弱的条件并证明其必要性 / 充分性。这种思想在有些构造题中同样适用,当然能在更强的条件下构造出解就不用证必要性了。

代码

/*
以下为自主做题时的思路: 
从大到小的最长被删前缀
看一个数后面是否有 >= rk 个删除操作
有就删,连带最近删除操作一起丢掉
考虑在中间插入了一个删除操作怎么办
让前面最近删掉的数匹配这个操作,然后让原来匹配的操作重新生效 
*/
#include <bits/stdc++.h>
#define ls(x) ((x) << 1)
#define rs(x) (((x) << 1) | 1)
#define mid ((l + r) >> 1)
using namespace std;
typedef long long ll;
const int N = 5e5 + 5;
int p[N],pos[N],q[N];
int mx[N << 2],tag[N << 2];
void push_up(int p){
	mx[p] = max(mx[ls(p)],mx[rs(p)]);
}
void add_tag(int p,int x){
	tag[p] += x;
	mx[p] += x;
}
void push_down(int p){
	if(!tag[p]) return;
	add_tag(ls(p),tag[p]),add_tag(rs(p),tag[p]);
	tag[p] = 0;
}
void add(int p,int l,int r,int L,int R,int k){
	if(l > R || r < L) return;
	if(l >= L && r <= R){
		add_tag(p,k);
		return;
	}
	push_down(p);
	add(ls(p),l,mid,L,R,k),add(rs(p),mid + 1,r,L,R,k);
	push_up(p);
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
//	freopen("attach.in","r",stdin);
//	freopen("attach.out","w",stdout);
	int n;
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>p[i];
		pos[p[i]] = i;
	}
	int res = n;
	add(1,1,n,1,pos[n],1);
	cout<<res<<' ';
	for(int i=1;i<n;i++){
		cin>>q[i];
		add(1,1,n,1,q[i],-1);
		while(mx[1] <= 0){
			res--;
			add(1,1,n,1,pos[res],1);
		}
		cout<<res<<' ';
	}
	return 0;
}
posted @ 2026-10-07 19:33  yutar  阅读(2)  评论(0)    收藏  举报