(t, n) 门限的组合构造
(t, n) 门限的组合构造
写在比较前面:年初搞出这个构造以为是 adhoc 的东西就丢到草稿箱里面了,然后就是这个结构越想越巧妙,在今年 CSP-S 初赛惊出现在到谋道程序填空题中,所以记录一下这个碎片模型的感觉。
对于 CSP-S 的程序填空题,好像很多人用更厉害的分析技巧做出来了,具体见 __Cby___ 写的文章 谁说完善程序一定要看题目描述的
标签:(t, n) 门限,组合,构造,CSP-S 2025 程序填空题
阅读顺序:从上到下
Part 1:怎么想到的?Threshold Scheme 和碎片模型
(t, n) 门限问题 是说我们怎么样可以将一个 秘密 拆解或分配给
- 任意
\ge t 个人合作可以 恢复 出原来的信息。 - 任意
<t 个人合作都 无法得到任何原来的信息。
很难不发现能否还原秘密在人数上有单调性,如果任意
我们考虑随便试试简单的情况,比如对于
- 我们可以把秘密拆成
3 个碎片a,b,c ,必须集齐所有碎片才能拼出原来的秘密。然后我们分配:- 第
1 个人拥有碎片a,b - 第
2 个人拥有碎片a,c - 第
3 个人拥有碎片b,c
- 第
- 这样每个人都差一个碎片,且任意两个人就可以凑齐所有的碎片。
分析一下碎片的本质,对于一个碎片
- 如果当前合作的人都是
\overline{A} 的 子集,则他们 无法 还原秘密。
所以设置碎片
于是我们可以设置碎片禁用掉所有大小为
- 具体地说:将秘密拆成
\binom{n}{t-1} 个碎片,对于每一个大小为t-1 的子集S,\;(S\subseteq\{1,\cdots,n\})(|S|=t-1) ,都恰好对应一个碎片a_S 表示只有 不在S 中的人才有碎片a_S 。 - 这里展示对于
5 个人,使得任意4 个人都可以还原秘密的构造:- 我们将秘密分成
10 个碎片,每个人拥有的碎片如下(每人一行): -
\begin{bmatrix}1&2&3&4\\ 1&&&&5&6&7\\&2&&&5&&&8&9\\&&3&&&6&&8&&10\\ &&&4&&&7&&9&10\end{bmatrix}
- 我们将秘密分成
正确性证明:
- 如果当前合作的人数
<t ,则我们先随便添加一些人将人数补成t-1 (加人肯定不会让能还原变成不能还原),那么t-1 个人构成的集合S ,根据构造,恰好定位到一个只有他们没有的碎片a_S ,所以他们无法还原秘密。 - 如果当前合作的人数
\ge t ,则因为每个碎片恰好t-1 人没有,根据抽屉原理,每个碎片他们之中至少有一个人有,于是他们能凑齐所有的碎片还原秘密。
这个 碎片模型 喵述了碎片禁用人的子集的限制关系,但是我们可以举一反三,对于 (t, n) 门限问题是固定了人数和门限数
- 碎片的作用是 禁用 一个人的子集。
- 任意
t-1 个人可以 恰好 定位到 一个 他们都 没有 的碎片。 -
下面两个问题分别介绍 “限制人和碎片” 和 “限制碎片和门限” 构造剩下一种的思路。
Part 2:CF1365G Secure Password
题目链接:https://codeforces.com/problemset/problem/1365/G
题意:这是一道交互题,有一个隐藏的数组
以下是解题思路:
我们希望询问能恰好拼出缺少一个位置,所以将每个位置看作碎片,每次询问表示一个人有哪些碎片,这里确定了人的数量,为了让碎片数量(支持的原题的位置数)更多,门限数量应该选择靠近一半的位置(这里用到的只有人可以唯一定位一个碎片的性质,所以门限不重要)。
有
然后每个人问它有的碎片的所有位置。
对于下标
Part 3:2025 CSP-S 初赛 程序填空 第二题
题意:有
以下是解题思路:我们从做题(不是破解填空题)的角度审视这个情况。
从需要唯一定位缺陷的物品可以想到利用碎片模型的唯一定位的性质,这里将物品看作碎片,一次测试(也就是客户)对应一个人,我们要确定最小的测试轮数
每个碎片都有可能是缺陷的,所以题目要求是每个碎片拥有它的人数不能超过
因为门限
程序中的,count_patterns(w, k) 是计算确定有 main 函数中第一步用这个函数枚举出最小的人数
18 // 计算长度为 w、1 的个数 ≤ k 的码字总数
19 long long count_patterns(int w, int k) {
20 long long total = 0;
21 for (int t = 0; t <= min(w, k); ++t) {
22 total += comb(w, t);
23 }
24 return total;
25 }
...
31 // === 第 1 步:求最小 w ===
32 int w = 1;
33 while (___①___) {
34 ++w;
35 }
36 cout << w << endl;
程序中的第二部就是用排列二进制来枚举所有碎片应该分给那些人。
38 // === 第 2 步:生成测试方案 ===
39 vector<vector<int>> code(n, vector<int>(w, 0));
40 int idx = 0;
41 for (int ones = 0; ones <= k && idx < n; ++ones) {
42 vector<int> bits(w, 0);
43 fill(bits.begin(), bits.begin() + ones, 1);
44 do {
45 for (int b = 0; b < w; ++b) {
46 code[idx][b] = bits[b];
47 }
48 ++idx;
49 if (idx >= n) {
50 break;
51 }
52 } while (std::___②___);
53 }
然后在第三步拿到每个人的结果,第四步根据任何人的集合可以唯一定位对应门限的一个碎片的性质,定位有问题的,这一步和 Part 2 Secure Password 中的定位是一样的。
64 // === 第 3 步:调用测试接口 ===
65 int signature = test_subset(plan);
66
67 // === 第 4 步:结果解码 ===
68 vector<int> sig_bits(w, 0);
69 for (int i = 0; i < w; ++i) {
70 if (___④___) {
71 sig_bits[i] = 1;
72 }
73 }
74
75 for (int j = 0; j < n; ++j) {
76 if (___⑤___) return j;
77 }
于是我们就从头解决了这个问题
。_。