题解:P8491 [IOI 2022] 囚徒挑战
这题是神秘提交答案题。其函数格式创到我了,说是返回 int[][],其实要返回 vector<vector<int>>。
首先可知,第一个人知道自己是第一个进入房间的人,因为他和他后面的人都一定不会把
先尝试打表,发现如果可传递的数字值域是
- 第一个人进入房间看到数字
0 打开袋子 A,如果a = 1 或者a = 4 ,则a 一定是最大值或最小值,否则在白板上写下数字1 ,表示他已经看过了袋子 A 且里面放的数字是2 或3 。 - 第二个人进入房间看到数字
1 ,他知道a \in \{2,3\} ,他查看b ,b 是1\sim 4 中的任意一个第二个人都能比较出a 和b 的大小关系。
可以发现,如果某一个袋子中的数字值域已经确定为
考虑白板数字值域为
- 第一个人进入房间打开袋子 A,如果
a=1 或a=10 ,则直接报告。否则如果a \in [2,5] ,则在白板上写数字1 ,是[6,9] 则写数字2 。 - 第二个人看到
1 或2 查看b 。考虑白板上原先是数字1 ,如果b \in [1,2]\cup[5,10] 则可以直接报告,否则在黑板上写数字3 。考虑白板上原先是数字2 ,如果b \in [1,6]\cup[9,10] 则可以直接报告,否则在黑板上写数字3 。 - 第三个人进入房间看到数字
3 ,检查a ,如果发现a \in [2,5] 则他会知道知道b \in [3,4] ,可以比较a,b 的大小;如果发现a \in [6,9] 则他会知道知道b \in [7,8] ,也可以比较a,b 的大小。
这么做其实相当于花费了
可以打出如下的表:
| ::::info[点我查看 |
||
|---|---|---|
::::
这个表中最重要的是如下算式:
也就是说,对于
::::info[点我查看代码]
#include<bits/stdc++.h>
//#define N 5590
using namespace std;
int maxn = 5588;
int g[] = {2,4,6,10,14,22,32,44,68,98,134,206,294,404,620,1190,1214,1862,3572,3644,5588};
int f[] = {0,1,1,2 ,2 ,2 ,2 ,2 ,3 ,3 ,3 ,3 ,3 ,3 ,3 ,3 ,3 ,3 ,3 ,3 ,3 };
int s[] = {1,4,4,4,7,7,7,10,10,10,13,13,13,16,16,16,18,18,20,20,20};
vector<vector<int> > ans;
// 已经确定另一个东西的值域是 lq~rq,我的值域是 lx~rx
void solve(int x,int lq,int rq,int lx,int rx,int idx){
int op_idx = !idx;
ans[x][0] = idx;
// 赋值判断大小
for(int i = lx;i <= lq;++i)ans[x][i] = -1 - idx;
for(int i = rq;i <= rx;++i)ans[x][i] = -1 - op_idx;
for(int b = 1;b <= f[20-x];++b){
int d = s[x] + b - 1;// 我要填入的数字
int nlx = lq + 1 + (b-1) * g[20-f[20-x]+1-s[x]],nrx = lq + b * g[20-f[20-x]+1-s[x]];
for(int i = nlx;i <= nrx;++i)ans[x][i] = d;
solve(d,nlx,nrx,lq,rq,!idx);
}
}
vector<vector<int> > devise_strategy(int N){
ans.resize(21);
for(int i = 0;i <= 20;++i)ans[i].resize(5590);
solve(0,1,5588,1,5588,0);
for(int i = 0;i <= 20;++i){
while(ans[i].size() != N+1)ans[i].pop_back();
}
return ans;
}
::::