题解: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;
}
}
}
}
}

浙公网安备 33010602011771号