「MCOI-07」Dream and More Discs

「MCOI-07」Dream and More Discs

首先考虑对于一个 \(x\)\(\leq x\) 的数构成什么结构:

image

对于所有红线左边的数,均 \(\leq x\),考虑我们只需要得到这些红线即可。

对于每个位置,初始时红线的可选区间为 \([0,2^m-1]\),接下来需要将红线的可选区间缩短。

这里我们采用二分的手法,考虑当前得到了红线为 \(mid\) 位置如下:

image

红线左边数的个数为 \(t=m_1+m_2+m_3+m_4\),且假设 \(x_1<x_2<x_3<x_4\)

显然,\(x_4\) 的排名不低于 \(t\)\(x_1\) 的排名不高于 \(t-n+1\)

首先考虑当 \(k \leq t\) 时,\(x_4\) 的排名必然 \(\geq k\),令 \(r_4 = m_4\)

\(k > t-n+1\)\(x_1\) 的排名必然 \(< k\),令 \(l_1 = m_1 + 1\)

显然两个条件必将满足至少一个,如此操作直到所有 \(l_i=r_i\) 即可。

然而这样会在已经存在某些 \(l_i=r_i\) 时出问题,我们需要忽略这些部分。

考虑当前对于 \(x_{\max}\) 的排名下界要减去 \(l_i=r_i\)\(x > x_{\max}\) 的部分。

同时 \(x_{\min}\) 的排名上界要加上 \(l_i=r_i\)\(x < x_{\min}\) 的部分。

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int n,m,k,Th;
long long a[60][1<<11];
int l[60],r[60],mid[60];
long long query(int id,int pos){
	if(a[id][pos]==0){
		cout<<"? "<<id<<" "<<pos<<endl;
		cin>>a[id][pos];
	}
	return a[id][pos];
}
struct Node{
	int pos;
	long long num;
}op[60];
bool operator <(const Node &lhs,const Node &rhs){
	return lhs.num<rhs.num;
} 
int main(){
	cin>>n>>m>>k>>Th;
	for(int i=1;i<=n;i++){
		a[i][0]=-1;
		l[i]=0;
		r[i]=(1<<m)-1;
	}
	while(true){
		int tot=0,sum=0;
		for(int i=1;i<=n;i++){
			mid[i]=(l[i]+r[i])>>1;
			sum+=mid[i];
			if(l[i]!=r[i]){
				op[++tot]=(Node){i,query(i,mid[i])};
			}
		}
		if(tot==0){
			break;
		}
		sort(op+1,op+tot+1);
		int tot_bigger=0;
		for(int i=1;i<=n;i++){
			if(l[i]==r[i]){
				if(query(i,l[i])>op[tot].num){
					tot_bigger++;
				}
			}
		}
		if(k<=sum-tot_bigger){
			r[op[tot].pos]=mid[op[tot].pos];
		}
		else{
			l[op[1].pos]=mid[op[1].pos]+1;
		}
	}
	int tot=0;
	for(int i=1;i<=n;i++){
		tot+=l[i];
	}
	while(tot>=k){
		int id=1;
		for(int i=2;i<=n;i++){
			if(query(i,l[i])>query(id,l[id])){
				id=i;
			}
		}
		if(tot==k){
			cout<<"! "<<id<<" "<<l[id]<<endl;
			return 0; 
		}
		else{
			l[id]--;
			tot--;
		}
	}
	return 0;
}
posted @ 2026-08-17 00:09  Oken喵~  阅读(3)  评论(0)    收藏  举报