1 条题解

  • 0
    @ 2026-8-6 23:03:31

    神秘题,感觉是我一辈子也会不了的那种。

    建图考虑先建一个随机的存在哈密顿路的图。

    首先我们先尝试构造一条哈密顿链。构造方法也很简单,一开始是 nn 个长度为 11 的链,然后钦定其中一条为主链,每次随机尝试将某一条链拼接到主链后面,因为图随机所以有 12\frac{1}{2} 的成功率,期望 22 步操作可以合并一条链。

    最后可能会剩下若干条链合并不了,因为每两条链都有 34\frac{3}{4} 的概率可以合并在一起(两种顺序,每种 12\frac{1}{2}),所以剩下的链的数量是 O(1)O(1) 条,我们随机劈开一条链然后重新尝试即可,因为一开始保证了存在哈密顿路所以一定可以成功,容易发现这一步需要期望 O(1)O(1) 次询问。

    然后我们还需要把这条链变成一个环,假设这条链原本是 uvu\to v 的,考虑找到链上最靠近 vvww 满足存在边 wuw\to u,此时 uwu\sim w 构成了一个环,然后尝试将 ww 后面的点按照链上的顺序插入这个环中。

    假设上一个插入的点是 xx(初始为 ww),xx 在环上的下一个点是 yy,这次要插入的点是 zz,那么尝试将如果存在 zyz\to y 的边,又因为原本链上存在 xzx\to z 的边,所以可以把 zz 插入 x,yx,y 中间。否则就存在 yzy\to z 的边,把 xx 换成 yy 重复这个过程。因为存在哈密顿路,所以图强连通,所以环上必然存在一个 yy 使得存在 zyz\to y 的边,也就是若干步之后一定可以成功。

    容易发现这个部分需要插入的点数是 O(1)O(1) 的,每次插入也是 O(1)O(1) 的,总询问次数 O(1)O(1)

    考虑这个方法的询问次数,第一部分构造链需要合并 nn 次,每次期望 22 个询问,第二部分链变成环期望 O(1)O(1) 个询问,所以我们就得到了一个期望 2n+O(1)2n+O(1) 次询问的做法。

    考虑优化,注意到合并两个长为 11 的链其实只需要 11 次询问且必然成功,所以我们一开始可以先合并成 n2\frac{n}{2} 条长度为 22 的链,然后在继续合并,此时操作次数就是 1.5n+O(1)1.5n+O(1) 了。

    但是因为限制是恰好 1.5n1.5n,所以多了 O(1)O(1) 次操作无法通过。

    考虑进一步优化,首先我们不再随机建图,考虑这样建图,首先构造一个 nn 元环,然后如果两个点 u,vu,vuu 出发顺势针走到 vv 的距离不超过 n2\frac{n}{2},那么连接 uvu\to v,否则连接 vuv\to u

    此时我们随机合并两条链的成功率依旧是 12\frac{1}{2}

    但是当我们尝试把 yy 开头的链拼到 xx 结尾的链后面失败之后,那么 xx 在那个环上顺时针到 yy 的距离一定超过 n2\frac{n}{2}。此时如果我们向 xx 后面成功拼接了一个长度为 22 的链,那么 xx 所在链的末尾就向在环上顺时针走 22 步,每步长度在 1n21\sim\frac{n}{2} 中随机,简单积分一下发现,此时后面能接上 yy 的概率从 12\frac{1}{2} 变成了 23\frac{2}{3}

    加入此优化后根据实现不同就可以做到 735740735\sim 740 次操作左右。

    当然,我们注意到,如果我们在每次合并失败,分裂一条链的时候,判断一下是否存在单点的链,如果存在就改为尝试将这个点按照以下方式插入主链中。

    假设主链是 sts\to t,单点是 uu

    如果存在 tut\to u,那么插入到结尾。如果存在 usu\to s 的边,那么插入到开头。否则链上一定存在两个相邻的位置 x,yx,y,使得存在 xu,uyx\to u,u\to y 的边,插入这两者中间。

    我们容易发现,这样只需要期望不超过 22 次询问,当然实际情况下因为无法插入时,插入结尾的情况通常已经判断过了,所以实际询问次数的期望更少。

    加入这个优化后可以做到 730730 次询问左右。具体的期望操作次数不是很算的清楚。根据实测的结果,按照直线拟合了一下我寻思操作次数是 1.447n+O(1)1.447n+O(1) 左右。

    代码:

    #include<bits/stdc++.h>
    #define N 509
    #define pb push_back
    #define ll long long
    using namespace std;
    mt19937 rd(chrono::steady_clock::now().time_since_epoch().count());
    int rnd(int x,int y){
    	ll t=rd();
    	return t%(y-x+1)+x;
    }
    int n;
    int a[N][N];
    int query(int x,int y){
    	if(x==y)return 0;
    	if(a[x][y]||a[y][x])return a[x][y];
    	cout<<"? "<<x<<" "<<y<<"\n";
    	char c;cin>>c;
    	if(c=='>')a[x][y]=1;
    	else a[y][x]=1;
    	return a[x][y];
    }
    vector<vector<int>> q,rj[2];
    void insert(vector<int> &A,int x){
    	if(query(A.back(),x))A.pb(x);
    	else if(query(x,A.front()))A.insert(A.begin(),x);
    	else{
    		for(int i=0;i+1<A.size();i++){
    			if(query(A[i],x)&&query(x,A[i+1])){
    				A.insert(A.begin()+i+1,x);return;
    			}
    		}
    	}
    }
    void split(){
    	int mx=0;for(auto t:q)mx=max(mx,(int)t.size());
    	if(mx<=1)return;int t=rnd(0,q.size()-1);
    	while(q[t].size()<=1)t=rnd(0,q.size()-1);
    	int x=rnd(1,q[t].size()-1);
    	q.emplace_back(q[t].begin()+x,q[t].end());
    	q[t].erase(q[t].begin()+x,q[t].end());
    }
    void cl(){
    	for(auto t:rj[0])q.pb(t);
    	for(auto t:rj[1])q.pb(t);
    	rj[0].clear();rj[1].clear();
    }
    void solve(){
    	q.clear();rj[0].clear();rj[1].clear();
    	for(int i=1;i<=n;i++){
    		for(int j=1;j<=n;j++)a[i][j]=0;
    	}
    	for(int i=1;i<=n;i+=2){
    		if(i+1<=n){
    			int t=query(i,i+1);
    			if(t)q.pb({i,i+1});
    			else q.pb({i+1,i});
    		}
    		else q.pb({i});
    	}
    	int ok=0,fl=0;
    	int u=0;
    	while(q.size()+rj[0].size()+rj[1].size()>1){
    		auto &r=rj[ok^1];
    		if(!r.empty()){
    			if(query(q[0].back(),r.back().front())){
    				for(int x:r.back())q[0].pb(x);
    				r.pop_back();ok^=1;
    				for(auto t:r)q.pb(t);
    				r.clear();
    			}
    			else{
    				q.pb(r.back());r.pop_back();++fl;
    			}
    			continue;
    		}
    		if(q.size()<=1)cl();
    		u=rnd(0,q.size()-1);
    		if(!u)continue;
    		if(query(q[0].back(),q[u].front())){
    			for(int x:q[u])q[0].pb(x);
    			q.erase(q.begin()+u);
    			ok^=1;
    			for(auto t:rj[ok])q.pb(t);
    			rj[ok].clear();
    		}
    		else if(++fl>=n&&fl>=5){
    			bool t=0;
    			for(int i=1;!t&&i<q.size();i++){
    				if(q[i].size()<=1){
    					t=1;
    					for(int x:q[i])insert(q[0],x);
    					q.erase(q.begin()+i);
    				}
    			}
    			if(!t)split();
    			fl=0;
    		}
    		else{
    			swap(q[u],q.back());
    			rj[ok].pb(q.back());
    			q.pop_back();
    		}
    	}
    	cl();vector<int> A=q.front();
    	int t=n-1;while(!query(A[t],A[0]))--t;
    	vector<int> ans;
    	for(int i=0;i<=t;i++)ans.pb(A[i]);
    	for(int i=t+1,j=t,k=0;i<n;i++){
    		while(!query(A[i],ans[k])){
    			j=(j+1)%i;k=(k+1)%i;
    		}
    		ans.insert(ans.begin()+j+1,A[i]);
    		j=j+1;k=(j+1)%(i+1);
    	}
    	cout<<"! ";for(int x:ans)cout<<x<<" ";cout<<"\n";
    }
    int main(){
    	int T;cin>>n>>T;
    	for(int i=1;i<=n;i++){
    		for(int j=i+1;j<=n;j++){
    			if(j-i<=(n/2))a[i][j]=1,a[j][i]=0;
    			else a[i][j]=0,a[j][i]=1;
    		}
    	}
    	for(int i=1;i<=n;i++)a[i][i%n+1]=1,a[i%n+1][i]=0;
    	for(int i=1;i<=n;i++){
    		for(int j=1;j<=n;j++)cout<<a[i][j],a[i][j]=0;
    		cout<<"\n";
    	}
    	while(T--)solve();return 0;
    } 
    
    • 1

    信息

    ID
    12557
    时间
    10000ms
    内存
    700MiB
    难度
    10
    标签
    递交数
    1
    已通过
    1
    上传者