Codeforces Round #700 (Div. 2) C(三分)
二分或三分。
三分:若 \((L,R)\) 有局部最小,取 m1=max(L+1,L+(R-L)/3) 、 m2=min(R-1,R-(R-L)/3) 。
若 \(a_{m_1}<a_{m_2}\and a_{m_1}<a_L\) ,则 \((L,m_2)\) 必有局部最小。
若 \(a_{m_2}<a_{m_1}\and a_{m_2}<a_R\) ,则 \((m_1,R)\) 有局部最小。
否则 \((m1,m2)\) 必有局部最小(因为一般情况下每次迭代都会保证中值小于两边界,单调增减的特殊情况不会执行到这一步)。
最坏情况下每次将区间缩减为原来的 \(\frac23\) ,总查询次数至多 \(2\lceil \log_{\frac32}n\rceil\le58\) ,比二分慢。
/**
* Codeforces Round #700 (Div. 2)
*/
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e5+5;
int a[N];
int get(int p)
{
if (a[p]) return a[p];
printf("? %d\n", p);
fflush(stdout);
scanf("%d", a+p);
return a[p];
}
void solve()
{
int n;
scanf("%d", &n);
a[0] = a[n+1] = INT_MAX;
int L = 0, R = n+1; // 记录边界位置(保证边界内必有局部最小)
while (L+2 < R) { // 边界内不止一个点时循环
int m1 = max(L+1, L+(R-L)/3);
int m2 = min(R-1, R-(R-L)/3);
if (get(m1) < get(m2)) {
if (get(m1) < get(L)) R = m2; // m1<左边界则右边界变为m2
else L = m1, R = m2; // 因为区间中点值比小于左右边界
}
else {
if (get(m2) < get(R)) L = m1;
else L = m1, R = m2;
}
// cout << '#' << L << '#' << R << endl;
}
printf("! %d\n", L+1);
fflush(stdout);
}
int main()
{
// ios::sync_with_stdio(0); cin.tie(0);
int t = 1;
// scanf("%d", &t);
while (t--) {
solve();
}
return 0;
}
/*
WA61的样例
26
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26(编号,方便观察)
12 8 5 1 3 4 6 7 9 10 13 14 15 16 18 19 20 2 11 17 21 22 23 24 25 26
*/
\(PS:\) 感谢网友的指正,想不到之前漏洞百出的做法居然能活到赛后。

浙公网安备 33010602011771号