P7998 题解
题目大意
有一个数
做法
考虑 DP,设
其中
先将每个状态
int n,dp1[N]; lf dp2[N];
signed main() {
dp2[0] = 0;
dp2[1] = 0;
printf("1\n");
rep(i, 2, 1e5) {
dp2[i] = INFINITY;
int mid = (1 + i) / 2;
re(j, i) {
int l = mid - (j - 1) / 2, r = l + j - 1,
res = max({l - 1, i - l, r - 1, i - r});
if (dp2[res] + 1.l / j < dp2[i])
dp2[i] = dp2[res] + 1.l / j, dp1[i] = j;
}
printf("%d\n",dp1[i]);
}
return 0;
}
你会发现这个表太大了导致提交不到 OJ 上。我们可以先把数据扔进 Excel 里可视化:
可以发现,数据中形成了几条直线,所以可以只将前
利用这个函数作为二分的决策点,可写出以下代码:
const int biao[11234]={/*省略数据表*/};
int F(int x) {
if (x <= 10000)
return biao[x];
if (x <= 13383)
return 4938;
if (x <= 19690)
return 7000;
if (x <= 27902)
return 9900;
if (x <= 39555)
return 14030;
if (x <= 55906)
return 19853;
if (x <= 79133)
return 28114;
return 39600;
}
void Ans(int x) {
cout << "! " << x << '\n' << flush;
exit(0);
}
pair<int, int> Ask(int l, int r) {
cout << "? " << l << ' ' << r << '\n' << flush;
int x, y;
cin >> x >> y;
return {x, y};
}
void ErFen(int l, int r) {
if (l == r)
Ans(l);
int len = r - l + 1, mid = (l + r) / 2, res = F(len),
L = mid - (res - 1) / 2, R = L + res - 1;
auto [u, v] = Ask(L, R);
if (v == 1)
Ans(u);
else if (v == 2)
ErFen(l, u - 1);
else
ErFen(u + 1, r);
}
代码量就降到了 48k,这就可以交到 OJ 上了。这个代码得到了 99pt,最后一个点超出标准次数
既然函数拟合的不够好,可以再用 DP 把函数上下扰动调整一下。DP 的第二维改成从
signed main() {
dp2[0] = 0;
dp2[1] = 0;
rep(i, 2, 1e5) {
dp2[i] = INFINITY;
int mid = (1 + i) / 2;
rep(j, max(1, F(i) - 5), min(i, F(i) + 5)) {
int l = mid - (j - 1) / 2, r = l + j - 1,
res = max({l - 1, i - l, r - 1, i - r});
if (dp2[res] + 1.l / j < dp2[i])
dp2[i] = dp2[res] + 1.l / j, dp1[i] = j;
}
}
cin >> n;
ErFen(1, n);
return 0;
}