洛谷 P16536 [THUPC 2026 决赛] 流光解密 题解

谨以此题解纪念第一道通信题。

传送门 洛谷 P16536 [THUPC 2026 决赛] 流光解密

题意

本题为通信题。

给定一个数字和一棵树,执行两个任务。

  1. 通过给树边赋予方向来编码这个数,并将这个“有向树”传给任务二。

  2. 接受任务一得到的“有向树”,并还原给定的数字。接收时边的顺序会被打乱。

限制:

  • 树的结构和节点编号在两个任务间不变。

  • 任务一的编码是在线的,编码一条边后读取下一条边。

  • 节点数 \(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;
}
posted @ 2026-07-27 16:34  EtherealYz  阅读(9)  评论(0)    收藏  举报