专栏文章

题解:CF2163D2 Diadrash (Hard Version)

CF2163D2题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@min8gzpf
此快照首次捕获于
2025/12/01 22:17
3 个月前
此快照最后确认于
2025/12/01 22:17
3 个月前
查看原文
首先,这类题有一个经典trick,就是对于一个 [0,n1][0,n-1] 排列的某个子集的 mex\text{mex} 等价于其补集的 min\min。证明显然。
对于此题,已知一个集合的 mex\text{mex} 的值,如果我们往这个集合加一个数字,这个值一定不会变小。由此可以推断原题中所有被其他询问包含的询问都是无用的。此时,区间仅剩 O(N)O(N) 个。假定剩余 mm 个区间并且区间已经升序排序。
fi=minj=1ipjf_i = \min_{j=1}^{i}{p_j}gi=minj=inpjg_i = \min_{j=i}^{n}{p_j},显然 ffgg 分别是单调递减和单调递增的。同时可以得到 mexi=lrpi=min(fl1,gr+1)mex_{i=l}^{r}{p_i}=\min(f_{l-1},g_{r+1})。那么,对于所有询问得到的函数,一定是一个增函数和一个减函数对应位置的 min\min。如图:
红点就是最终答案所在位置,二分选哪个区间即可。
有个细节是答案有可能取在极值点两边,所以最终答案要选择极值点附近两点比较大小。
次数 2×log2n+22\times \log_2{n}+2
CPP
#include<bits/stdc++.h>
using namespace std;
const int N=3e5+10;
int a[N],n,q;
int ask(int l,int r){
	cout<<"? "<<l<<" "<<r<<endl;
	int ret;
	cin>>ret;return ret;
}
int l[N],r[N],m;
signed main(){
	ios::sync_with_stdio(false);cin.tie(0);
	int T;cin>>T;
	while(T--){
		cin>>n>>q;
		for(int i=1;i<=n;i++)a[i]=i+1;
		while(q--){
			int l,r;cin>>l>>r;
			a[r]=min(a[r],l);
		}
		int mn=n+1;
		for(int i=n;i>=1;i--){
			if(a[i]>=mn)a[i]=i+1;
			else mn=min(a[i],mn);
		}
		m=0;
		for(int i=1;i<=n;i++)if(a[i]!=i+1)l[++m]=a[i],r[m]=i;//所有有用的区间
		int p=0;
		for(int i=1<<20;i;i>>=1){
			if(p+i<=m&&ask(l[p+i],n)>=ask(1,r[p+i]))p+=i;
		}
		int ans=0;
		if(p!=0)ans=max(ans,ask(l[p],r[p]));
		if(p+1<=m)ans=max(ans,ask(l[p+1],r[p+1]));
		cout<<"! "<<ans<<endl;
	}
	return 0;
}

评论

0 条评论,欢迎与作者交流。

正在加载评论...