UOJ #758. 【IOI2022】囚徒挑战
奇妙的题目?
首先显然可以拆成二进制按位比较。具体的,第一个人先看 \(A\) 最高位是什么,然后写在白板上,然后第二个人看 \(B\) 最高位是什么,和写在白板上的数字比较,以此类推。需要\(3\log n\)次。
我们发现第二个人只比较不干事有点浪费,不妨采取这样的行动:第一个人看\(A\)最高位,卸载黑板上,然后第二个人看\(B\)的最高位,比较,然后看次高位,写在黑板上,以此类推。当前看什么数可以通过当前轮数模 \(2\) 的余数确定。这样需要\(2\log n\)次。
因为\(3^2>2^3\),因此可以用三进制代替二进制,可以做到\(24\)。
考虑三进制最后一位数的过程,发现如果最后一位是\(0\)那么一定这个数小,如果是\(2\)则一定这个数比较大。则只需要传递一位的信息下去,可以做到\(22\)位。
发现这个性质不止可以在最后一位用,可以每一位都用,将值域去掉最小的和最大的两个,这样会变成\(21\)位。
还有一位,没办法了,怎么办呢,打个表先!
发现最后剩下 \(4\) 个数的时候我们还是用三进制去做非常浪费,这个可以直接用两个数做掉。因此可以做到\(20\)次了。
#include "prison.h"
#include<bits/stdc++.h>
#define Gc() getchar()
#define Me(x,y) memset(x,y,sizeof(x))
#define Mc(x,y) memcpy(x,y,sizeof(x))
#define d(x,y) ((m)*(x-1)+(y))
#define R(n) (rnd()%(n)+1)
#define Pc(x) putchar(x)
#define LB lower_bound
#define UB upper_bound
#define PB push_back
using ll=long long;using db=double;using lb=long db;using ui=unsigned;using ull=unsigned ll;
using namespace std;const int N=5e3+5,M=N*4+5,K=2e3+5,mod=998244353,Mod=mod-1;const db eps=1e-9;const ll INF=1e18+7;mt19937 rnd(time(0));
vector<vector<int>> s;vector<int> Ts;
void calc(int l,int r,int L,int R,int d,int op,int La){
int v=(d-1)*3+op,i;s[v][0]=d&1;
for(i=l;i<=r;i++) s[La][i]=v;
for(i=L;i<=l;i++) s[v][i]=-1-(d&1);
for(i=r;i<=R;i++) s[v][i]=-2+(d&1);
l++;r--;if(l>r) return;
if(l+3>=r){
int m=l+r>>1;calc(l,m,l-1,r+1,d+1,1,v);calc(m+1,r,l-1,r+1,d+1,2,v);
}else{
int m1=(l*2+r)/3,m2=(l+r*2)/3;
calc(l,m1,l-1,r+1,d+1,1,v);calc(m1+1,m2,l-1,r+1,d+1,2,v);calc(m2+1,r,l-1,r+1,d+1,3,v);
}
}
vector<vector<int>> devise_strategy(int N) {
for(int i=0;i<=N;i++) Ts.PB(0);for(int i=0;i<=20;i++) s.PB(Ts);
calc(1,N,1,N,0,3,0);
//for(auto i:s) {for(int j:i) cerr<<j<<' '; cerr<<'\n';}
return s;
}