[IOI2022] 囚徒挑战 做题记录
容易想到一位一位比大小。根据百万富翁那个题,我们不一定用二进制,设为 \(b\) 进制。
显然 \(b\) 取 \(e\) 时最优,\(e\) 更接近 \(3\),所以这里 \(b\) 取 \(3\)。
观察一下状态数:需要记录比较哪一位,以及这一位的数值,或者这一位已经比较完毕,状态数为 \((b + 1)\lceil \log_b n \rceil\),上界 \(m = 32\)。
思考有没有优化的空间,我们可以两个数每一位交替比较,这样就节省了表示该位已经比较完毕的状态,状态数为 \(b\lceil \log_b n \rceil\),上界 \(m = 24\)。
此时已经足够优秀,感觉还需要一定的小优化。
对于最后一位,如果为 \(0/2\),就可以直接得到比较结果,此时上界为 \(m = 22\)。
这其实对我们有一定启发,思考是否存在更优美的形式(从特殊到一般)。对于一个值域区间 \([l, r]\),如果询问出来为 \(l\) 或 \(r\) 就可以直接返回,然后在 \([l + 1, r - 1]\) 上平均分成三段,这样可以做到 \(m = 21\)。
还需优化一次,考虑常数细节,当需划分的长度为 \(2\) 时可以直接作为一段,当需划分的长度为 \(3/4\) 时仅需划分为两段,这样就能做到 \(m = 20\) 了。
像这种策略题,需要先构建出一个大致的思路方向,然后剪掉不必要的部分。
对于一些比较特殊化的部分,思考如何转化为更具有普遍性的结论。
需要注意一定的常数优化。
点击查看代码
#include <bits/stdc++.h>
#define ll int
#define LL long long
#define ull unsigned ll
#define fi first
#define se second
#define mkp make_pair
#define pir pair <ll, ll>
#define pb push_back
#define i128 __int128
using namespace std;
char buf[1 << 22], *p1, *p2;
// #define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, (1 << 22) - 10, stdin), p1 == p2)? EOF : *p1++)
template <class T>
const inline void rd(T &x) {
char ch; bool neg = 0;
while(!isdigit(ch = getchar()))
if(ch == '-') neg = 1;
x = ch - '0';
while(isdigit(ch = getchar()))
x = (x << 1) + (x << 3) + ch - '0';
if(neg) x = -x;
}
const ll maxn = 5010, inf = 1e9, mod = 1000002022;
ll power(ll a, ll b = mod - 2) {
ll s = 1;
while(b) {
if(b & 1) s = 1ll * s * a %mod;
a = 1ll * a * a %mod, b >>= 1;
} return s;
}
template <class T, class _T>
const inline ll pls(const T x, const _T y) { return x + y >= mod? x + y - mod : x + y; }
template <class T, class _T>
const inline ll mus(const T x, const _T y) { return x < y? x + mod - y : x - y; }
template <class T, class _T>
const inline void add(T &x, const _T y) { x = x + y >= mod? x + y - mod : x + y; }
template <class T, class _T>
const inline void sub(T &x, const _T y) { x = x < y? x + mod - y : x - y; }
template <class T, class _T>
const inline void chkmax(T &x, const _T y) { x = x < y? y : x; }
template <class T, class _T>
const inline void chkmin(T &x, const _T y) { x = x < y? x : y; }
vector <ll> a[22]; ll m, id[55][3];
vector <vector <ll> > s;
pir Get(ll l, ll r) {
if(r > l + 3) {
ll x = (r - l + 1) / 3, y = (r - l + 1 - x) / 2;
x += l - 1, y += x;
return mkp(x, y);
} else if(r > l + 1) return mkp(l + r >> 1, r);
else return mkp(r, r);
}
void dfs(ll l, ll r, ll L, ll R, ll u, ll c) {
if(!c) {
a[u][l] = -1, a[u][r] = -2;
if(r == 2) return;
pir t = Get(l + 1, r - 1);
ll x = t.fi, y = t.se;
id[0][0] = ++m;
if(x < y) id[0][1] = ++m;
if(y < r - 1) id[0][2] = ++m;
for(ll k = l + 1; k < r; k++)
if(k <= t.fi) a[u][k] = id[0][0];
else if(k <= t.se) a[u][k] = id[0][1];
else a[u][k] = id[0][2];
dfs(l + 1, x, l, r, id[0][0], 1);
if(x < y) dfs(x + 1, y, l, r, id[0][1], 1);
if(y < r - 1) dfs(y + 1, r - 1, l, r, id[0][2], 1);
} else {
ll z = c & 1; a[u][0] = z;
for(ll k = L; k < l; k++) a[u][k] = -z - 1;
for(ll k = r + 1; k <= R; k++) a[u][k] = -(z ^ 1) - 1;
if(l == r) return;
a[u][l] = -z - 1, a[u][r] = -(z ^ 1) - 1;
if(l + 1 == r) return;
pir t = Get(l + 1, r - 1);
ll x = t.fi, y = t.se;
if(id[c][0] == -1) id[c][0] = ++m;
if(x < y && id[c][1] == -1) id[c][1] = ++m;
if(y < r - 1 && id[c][2] == -1) id[c][2] = ++m;
for(ll k = l + 1; k < r; k++)
if(k <= x) a[u][k] = id[c][0];
else if(k <= y) a[u][k] = id[c][1];
else a[u][k] = id[c][2];
dfs(l + 1, x, l, r, id[c][0], c + 1);
if(x < y) dfs(x + 1, y, l, r, id[c][1], c + 1);
if(y < r - 1) dfs(y + 1, r - 1, l, r, id[c][2], c + 1);
}
}
vector <vector <ll> > devise_strategy(ll N) {
memset(id, -1, sizeof id);
for(ll i = 0; i <= 20; i++) a[i].resize(N + 1);
dfs(1, N, 0, 0, 0, 0);
for(ll i = 0; i <= m; i++) s.pb(a[i]);
return s;
}

浙公网安备 33010602011771号