P8111 [Cnoi2021] 区间
[Cnoi2021] 区间
题目背景
Cirno 有一个区间
每次,你可向Cirno询问一个数字
题目描述
为了猜到这个区间,你需要实现一个函数 std::pair<int,int> Guess(int n,int c)
,这个函数的作用是在不超过 std::pair<int,int>
的形式返回。
你可以调用交互库中一个叫做 Query
的函数,其原型为 int Query(int x)
,返回值为:
- 若
,返回 。 - 若
,返回 。 - 若
,返回 。
你调用 Query
函数的次数不超过
在一个测试点中,你的 Guess
函数可能被调用多次,最多不超过 void init()
,这个函数只会在开始时被交互库调用一次。当然,它的实现可以为空。
Bonus:
Solution
很妙的一个题。
最简单的方式就是分别二分确定
会发现其实二分
假设第一步询问的位置是
个人觉得不是很好写。
Code(100pts)
#include <bits/stdc++.h>
using namespace std;
int Query(int x);
void init() {}
pair<int, int> Guess(int N, int total) {
if (N == 1) return make_pair(1, 1);
int L = 1, R = N;
int l = 1, r = N, mid = (l + r) / 3, nl, nr;
int d = Query(mid);
if (d == -1) l = mid + 1;
else r = mid - 1, L = mid;
if (d != 1) nl = mid + 1, nr = N, R = mid;
else nr = mid - 1, nl = 1;
if (d == -1) {
mid = (l + r) >> 1;
d = Query(mid);
if (d != 1) nl = mid + 1, nr = N, R = mid;
else nr = mid - 1, nl = l;
if (d == -1) l = mid + 1;
else r = mid - 1, L = mid;
}
while (l <= r) {
mid = (l + r) >> 1;
if (Query(mid) == -1) l = mid + 1;
else r = mid - 1, L = mid;
}
l = nl, r = nr;
while (l <= r) {
mid = (l + r) >> 1;
if (Query(mid) != 1) l = mid + 1, R = mid;
else r = mid - 1;
}
return make_pair(L, R);
}
Bonus
考虑 DP,设
- 返回
,那么最优的询问方法一定是 。 - 返回
,那么最优的询问方法一定是 。 - 返回
,那么 和 相当于是按照 分割成了两半,相互独立,因此直接在左侧和右侧二分就是最优解。
也就是:
时间复杂度
Code(100pts)
#include <bits/stdc++.h>
#define For(i, a, b) for (int i = (a); i <= (b); ++i)
#define Rof(i, a, b) for (int i = (a); i >= (b); --i)
using namespace std;
int Query(int x);
const int _N = 1500 + 5;
int f[_N], p[_N];
void init() {
memset(f, 0x3f, sizeof f);
f[1] = 0, p[1] = 1;
f[0] = 0, p[0] = 1;
For(i, 2, 1500) {
For(j, 1, i) {
int cnt = ceil(log2(j)) + ceil(log2(i - j + 1));
int val = max({f[j - 1], f[i - j], cnt}) + 1;
if (val < f[i])
f[i] = val, p[i] = j;
}
}
}
pair<int, int> Solve2(int L, int R, int pos) {
int l, r, mid;
pair<int, int> res;
l = L, r = pos - 1;
while (l <= r) {
mid = (l + r) >> 1;
if (Query(mid) == -1) l = mid + 1;
else r = mid - 1;
}
res.first = l;
l = pos + 1, r = R;
while (l <= r) {
mid = (l + r) >> 1;
if (Query(mid) == 1) r = mid - 1;
else l = mid + 1;
}
res.second = r;
return res;
}
pair<int, int> Solve1(int L, int R) {
if (L == R) return {L, R};
int pos = p[R - L + 1] + L - 1;
int res = Query(pos);
if (res == -1) return Solve1(pos + 1, R);
if (res == 1) return Solve1(L, pos - 1);
return Solve2(L, R, pos);
}
pair<int, int> Guess(int N, int C) {
return Solve1(1, N);
}
容易发现复杂度瓶颈其实是在 DP 部分,考虑观察决策点有什么性质。打表发现,记
Code(101pts)
#include <bits/stdc++.h>
using namespace std;
int Query(int x);
void init() {}
inline int GetPos(int i) {
int lim = 1 << __lg(i);
return (i & ((lim >> 1) - 1)) + 1;
}
pair<int, int> Solve2(int L, int R, int pos) {
int l, r, mid;
pair<int, int> res;
l = L, r = pos - 1;
while (l <= r) {
mid = (l + r) >> 1;
if (Query(mid) == -1) l = mid + 1;
else r = mid - 1;
}
res.first = l;
l = pos + 1, r = R;
while (l <= r) {
mid = (l + r) >> 1;
if (Query(mid) == 1) r = mid - 1;
else l = mid + 1;
}
res.second = r;
return res;
}
pair<int, int> Solve1(int L, int R) {
if (L == R) return {L, R};
int pos = GetPos(R - L + 1) + L - 1;
int res = Query(pos);
if (res == -1) return Solve1(pos + 1, R);
if (res == 1) return Solve1(L, pos - 1);
return Solve2(L, R, pos);
}
pair<int, int> Guess(int N, int C) {
return Solve1(1, N);
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
2022-12-02 OI 中的多项式(或许完工?)