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:\) 感谢网友的指正,想不到之前漏洞百出的做法居然能活到赛后。

posted @ 2021-02-08 15:04  Zewbie  阅读(173)  评论(3)    收藏  举报