洛谷 P16536 [THUPC 2026 决赛] 流光解密 题解
谨以此题解纪念第一道通信题。
传送门 洛谷 P16536 [THUPC 2026 决赛] 流光解密 。
题意
本题为通信题。
给定一个数字和一棵树,执行两个任务。
-
通过给树边赋予方向来编码这个数,并将这个“有向树”传给任务二。
-
接受任务一得到的“有向树”,并还原给定的数字。接收时边的顺序会被打乱。
限制:
-
树的结构和节点编号在两个任务间不变。
-
任务一的编码是在线的,编码一条边后读取下一条边。
-
节点数 \(3\le n\le 30\) ,给定的数 \(1\le s\le 2^{n-1}\)
思路
容易注意到这个数据范围时刻在提醒我们把这个数 \(s\) 按二进制位来处理。我们考虑到 \(s\) 最多有 \(n\) 位,而仅通过树的节点是无法承载每个 bit 的信息的。原因是我们无法直接在两个任务间传递点权。发现边有正反向之分,且可以在两任务间传递,所以我们考虑构造一种编码策略,用边的方向来承载每个 bit 。
所以我们不难想到,先将 \(s\) 减一,这样 \(s\) 的 bit 数恰好等于边数。然后考虑编码策略。经过我们孜孜不倦的尝试,我们发现想仅仅通过边的方向来编码也是困难的。难点在于,编码过程是在线的,且编码和解码时,接收边的顺序是不同的。我们无法将边的信息对应到某个确定的 bit 位。并且想到既然通过树来编码,应该不可能不使用点,所以我们考虑继续转换思路,同时使用节点和树边。
考虑这样做有一个明显的难点:我们需要通过点权来编码每个 bit ,但只能通过边来传递 \(0\) 或 \(1\) 。这时我们注意到:树的结构在两个任务间保持一致,且由于刚才我们进行了 \(s-1\) ,此时点数比 bit 数恰好多 \(1\) 。所以空出来的这个点就可以成为两个任务的桥梁。发现这一点后,接下来的思路就十分自然了。
编码阶段:我们考虑把 \(1\) 号点空出来,用 \(2\sim n\) 号点的点权来存储 \(s-1\) 的每个 bit 。特别地,令 \(1\) 号点点权为 \(0\) 。同时我们为每条边 \((u,v)\) 赋予边权 \(w\) :\(w=1\) 代表 \(u,v\) 点权相等,否则点权不等。接下来只要用方向来编码边权即可。我们设 \(u<v\) ,则可以用 \((u,v)\) 来表示 \(w=1\) ,\((v, u)\) 来表示 \(w=0\) 。
解码阶段:我们接收到了每一条树边的边权,同时我们知道 \(1\) 号点的点权为 \(0\) ,于是通过一次 dfs 就可以还原每个点的点权。还原点权后,我们就可以还原出 \(s-1\) 的二进制表示,进而得到 \(s\) 。
于是这道题就结束了。
代码
#include<iostream>
#include<cstring>
#include<vector>
#define MIKU 0
using namespace std;
int T, Q;
namespace Code {
int n, s, tmp, w[35];
void work() {
cin>>n>>s; tmp = s - 1;
w[1] = 0;
for(int i=2; i<=n; i++) {
if(tmp & (1<<(i-2))) w[i] = 1;
else w[i] = 0;
}
for(int i=1; i<n; i++) {
int u, v; cin>>u>>v;
if(w[u] == w[v]) cout<<u<<' '<<v<<endl; //小->大,边权 1,两端等
else cout<<v<<' '<<u<<endl; //大->小,边权 0,两端不等
}
}
void init() { memset(w, 0, sizeof(w)); }
void main() { while(T--) { init(); work(); } }
}
namespace Decode {
struct Edge { int v, w, nxt; } G[65];
int h[35], tot;
void addE(int u, int v, int w) { G[tot] = {v, w, h[u]}; h[u] = tot ++; }
int n, res, ndw[35];
void dfs(int u, int fa) {
for(int e=h[u]; ~e; e=G[e].nxt) {
int v = G[e].v, w = G[e].w;
if(v == fa) continue;
if(w) ndw[v] = ndw[u];
else ndw[v] = ndw[u] ^ 1;
dfs(v, u);
}
}
void work() {
cin>>n;
ndw[1] = 0;
for(int i=1; i<n; i++) {
int u, v; cin>>u>>v;
int w = (u < v);
addE(u, v, w), addE(v, u, w);
}
dfs(1, 0);
for(int i=2; i<=n; i++) if(ndw[i]) res |= (1 << (i-2));
cout<<res + 1<<endl;
}
void init() {
res = tot = 0;
memset(h, -1, sizeof(h));
memset(ndw, 0, sizeof(ndw));
}
void main() { while(T--) { init(); work(); } }
}
int main() {
cin>>T>>Q;
if(Q == 1) Code::main();
else Decode::main();
return MIKU;
}

浙公网安备 33010602011771号