题解:P11315 [RMI 2021] 速通 / Speedrun

前言

还是通信题好玩。

但是这个不能用全局变量的限制非常讨厌,在实现上可能会带来一些困扰。

QOJ 有提交地址:here.

思路分析

为了方便,我们称得到信息的人为 \(A\),试图破译信息的人为 \(B\)

首先分析 \(A\) 能在每个节点给 \(B\)\(20\) 位 01 串,再根据 \(n \le 10^3\),我们猜测这 \(20\) 位 01 串大概是两个树上的信息。

再分析 \(q\le 2\cdot 10^3\),感觉上,我们猜测大概是允许每个节点失配两次,而不是在两个节点失配 \(n\) 次。

又发现 \(B\) 的起点是随机的,为了能让 \(B\) 找到树根,我们需要记录每个节点的父亲,这样 \(B\) 就能一路跳到根节点。

于是我们现在需要考虑的是,在 \(B\) 已知树根和遍历到的每个点的父亲的情况下,该记录哪一个信息,才能使得 \(B\) 可以在每失配两次以内遍历全树。

到这一步没有太好的条件了,只能试了。

经过若干尝试,我们发现记录每个节点在 dfs 序上的下一个节点是一个很有前途的想法,于是我们尝试进一步分析:

  • 如果 \(x\) 在 dfs 序上的下一位 \(y\)\(x\) 相邻,那么我们直接能走到 \(y\)

  • 否则,根据我们 dfs 的过程,我们需要一直跳 \(x\) 的祖先,同时检查 \(y\) 是否和祖先相邻,如果相邻就能走过去。

根据我们 dfs 的过程,每条边至多被经过两次,也就是说失配的总次数不会超过 \(2n\)

于是我们做完了。

代码实现

其实正题刚刚开始。

因为这个题的独特格式,我们需要把 \(A\)\(B\) 两个函数写在一起,所以 judger 应该是禁止调用全局变量的,所以我们整个 dfs 的过程需要在函数内部存图,然后把存图的地址传进函数里,同时提供若干地址,让函数把树的信息写进这些地址里。

听起来很麻烦,实际上就是函数需要全程传址。

最难受的是不能在本地调试,评测返回的信息又有限。

感兴趣的可以自己上手写一写。

#include<bits/stdc++.h>
#include"speedrun.h"
using namespace std;
void dfs(vector<vector<int> > &v,int *dfn,int *rnk,int x,int fa,int len,int &cnt){
	for(int i=1;i<=len;i++){
		if(fa&(1<<(i-1))) setHint(x,i,1);
		else setHint(x,i,0);
	}
	cnt++;
	dfn[x]=cnt;
	rnk[cnt]=x;
	for(int i=0;i<v[x].size();i++){
		int y=v[x][i];
		if(y==fa) continue;
		dfs(v,dfn,rnk,y,x,len,cnt);
	}
}
void assignHints(int id,int n,int x[],int y[]){
	int len;
	if(id==1) setHintLen(min(n,20)),len=min(n,20)/2;
	else setHintLen(20),len=10;
	vector<vector<int> > v;
	vector<int> t[1005];
	int dfn[1005],rnk[1005],cnt=0;
	memset(dfn,0,sizeof(dfn));
	memset(rnk,0,sizeof(rnk));
	for(int i=1;i<n;i++){
		t[x[i]].push_back(y[i]);
		t[y[i]].push_back(x[i]);
	}
	for(int i=0;i<=n;i++){
		v.push_back(t[i]);
	}
	dfs(v,dfn,rnk,1,0,len,cnt);
	for(int i=1;i<=n;i++){
		for(int j=1;j<=len;j++){
			if(rnk[dfn[i]+1]&(1<<(j-1))) setHint(i,len+j,1);
			else setHint(i,len+j,0);
		}
	}
}
void speedrun(int id,int n,int s){
	int len=getLength()/2;
	while(1){
		int fa=0;
		for(int i=1;i<=len;i++){
			if(getHint(i)) fa|=(1<<(i-1));
		}
		if(fa){
			goTo(fa);
			s=fa;
		}else{
			break;
		}
	}
	while(1){
		int nxt=0;
		for(int i=1;i<=len;i++){
			if(getHint(i+len)) nxt|=(1<<(i-1));
		}
		if(!nxt) break;
		if(goTo(nxt)){
			s=nxt;
		}else{
			while(1){
				int fa=0;
				for(int i=1;i<=len;i++){
					if(getHint(i)) fa|=(1<<(i-1));
				}
				goTo(fa);
				s=fa;
				if(goTo(nxt)){
					s=nxt;
					break;
				}
			}
		}
	}
}
posted @ 2025-04-04 19:27  _Kenma  阅读(30)  评论(0)    收藏  举报