1 条题解
-
0
自动搬运
来自洛谷,原作者为

Alex_Wei
**搬运于
2025-08-24 22:36:17,当前版本为作者最后更新于2022-02-22 15:10:40,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
两种人, 或 。 只说真话, 对于询问相同的人的状态的回答相同。进行不超过 轮询问,每轮询问形如向第 个人问第 个人的状态。需保证 互不相同且 。已知 的人数严格大于 的人数,试确定每个人的状态。
高妙构造题。
首先,我们只能从回答的结果反推出每个人的状态,且一定是让两个人相互询问。若不相互询问,我们难以确定任何一个人的状态,因此导致获得的信息完全无用。
考虑两个 相互询问,回答必然是 。 相互询问,回答 至少有一个 。两个 相互询问,回答可能是任何结果。容易发现若回答为 ,则两个人的状态必然相同。
根据上述结论,我们考虑将所有人划分为若干 等价类,每个等价类内部所有人的状态均相同。一开始,每个人都是一个等价类。称为 一型等价类。
接下来,将所有一型等价类两两配对并互相询问。若询问结果不是 ,说明它们当中必然 至少有一个 。将这些 大小相同 的 等价类对 两两丢到一边,称为 二型等价类。
每次配对的过程中,可能有单独的一型等价类剩余。丢到一边,称为 三型等价类。
- 性质 1:任何时候任何一型等价类和三型等价类的大小均为 的幂。
- 性质 2:不会出现大小相同的三型等价类。
- 性质 3:一型等价类和三型等价类的 的个数严格大于 的个数。这是因为二型等价类中 不少于 (关键性质)。
考虑配对的最终结果。显然,若一型等价类个数 ,则还可以继续配对。若一型等价类个数等于 ,则看做三型等价类。
因此,最终不剩余任何一型等价类。根据上述三条性质,容易发现必然存在三型等价类,且最大的的三型等价类必然为 。若不然,根据小于 的不同的 的幂之和最大为 这一事实,与性质 3 矛盾。
现在我们用较少的步数确定了至少一个 。只需要用这个 去 倍增 确定所有二型等价类的类型即可。倍增的思想体现为每确定一个等价类的类型,若该等价类为 ,则每次能够确定的等价类的类型会增加该等价类的数量。
除非运气特别差, 次询问足以确定所有人的状态,因为最差结果是问出来的结果都是二型等价类,唯一的三型等价类大小为 ,且一开始用三型等价类问二型等价类的状态,得到的结果全是 。这种情况在加入适当随机化之后出现概率可以视为 。
#include <bits/stdc++.h> using namespace std; #define vint vector <int> #define pb push_back string query(vint p) { string res; cout << "test "; for(int it : p) cout << it << " "; cout << endl, cin >> res; return res; } const int N = 100 + 5; int n; vector <int> id[N], t1, t2, t3, fix; void merge(vint &x, vint y) {for(int it : y) x.push_back(it);} void solve() { cin >> n, t1.clear(), t2.clear(), t3.clear(), fix.clear(); for(int i = 1; i <= n; i++) id[i].clear(), id[i].pb(i), t1.pb(i); while(t1.size()) { vector <int> p(n), nt1; for(int i = 0; i + 1 < t1.size(); i += 2) p[t1[i] - 1] = t1[i + 1], p[t1[i + 1] - 1] = t1[i]; if(t1.size() & 1) t3.pb(t1.back()); string res = query(p); for(int i = 0; i + 1 < t1.size(); i += 2) { int x = t1[i], y = t1[i + 1]; if(res[x - 1] == '1' && res[y - 1] == '1') merge(id[x], id[y]), nt1.pb(x); else t2.pb(x), t2.pb(y); } t1 = nt1; } int bk = t3.back(), cur = 0; merge(fix, id[bk]), t3.pop_back(); vector <int> p(n); for(int it : t3) p[id[bk][cur++] - 1] = it; string res = query(p); cur = 0; for(int it : t3) if(res[id[bk][cur++] - 1] == '1') merge(fix, id[it]); while(t2.size()) { int lim = min(fix.size(), t2.size()); vector <int> p(n), nfix = fix; for(int i = 0; i < lim; i++) p[fix[i] - 1] = t2.back(), t2.pop_back(); string res = query(p); for(int i = 0; i < lim; i++) if(res[fix[i] - 1] == '1') merge(nfix, id[p[fix[i] - 1]]); fix = nfix; } string ans; for(int i = 0; i < n; i++) ans += '0'; for(int it : fix) ans[it - 1] = '1'; cout << "answer " << ans << endl; } int main() { int T; cin >> T; while(T--) solve(); }优化:注意到当且仅当最后一次合并时,将偶数个大小为 的一型等价类两两(组成一对)丢到二型等价类时,最大三型等价类较小,不大于 ,且三型等价类大小总和小于 。但注意到此时对于所有最大的二型等价类 对 ,必然恰有一个为 ,恰有一个为 。因为若两个都为 ,则可以推出 的个数不大于 的个数,与题意矛盾。这样一来,每次我们必然能成功获得一个较大的为 的等价类,优化显著。
一个乱搞方法(来源于 LOJ 第一篇提交):考虑每次将两个等价类合并。对于所有不同的等价类对 ,按 从大到小排序,并优先选择能操作的等价类对询问。在不超过 次询问内能得到答案,此时大小最大的等价类即为 ,其它等价类均为 (若为 就被最大等价类合并了)。
启示:构造题当中 的幂一般很有性质。
- 1
信息
- ID
- 7481
- 时间
- 9000ms
- 内存
- 1024MiB
- 难度
- 7
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者