ABC093D Worst Case

题目链接

思路

二分答案。
首先确定答案的上界。
不妨设 \(A \le B\),若 \(x y < AB\) 则 $x ,y $ 中至少有一个要小于 \(B\),故两场比赛排名之积小于 \(AB\) 的人至多有 \(2(B - 1)\) 个。

问题转化为:如何判断是否可能有 \(n\) 个人两场比赛的排名的乘积小于 \(AB\)
不难证明,最有利的情况是:不计 Takahashi,第一场比赛的前 \(n\) 名也是第二场比赛的前 \(n\) 名,并且在这 \(n\) 个人之中,第一场比赛排名第一的那个人第二场比赛排名第 \(n\),第一场比赛排名第二的那个人第二场比赛排名第 \(n - 1\),以此类推。举例言之,设 \(A = 2, B= 3, n = 5\),最有利的情况是这五个人两场比赛的排名分别为 \((1, 6), (3, 5), (4, 4), (5, 2), (6, 1)\)。问题化为:计算这种情况下名次乘积的最大值。这相当于求一个自变量是正整数的分段函数的最大值,这个函数最多有三段,每一段都是一个开口向下的二次函数,形如 \(x( s - x)\)。需要算出每一段的左右端点和每一段上 \(s\) 的值。

代码

int main() {
#if defined LOCAL && !defined DUIPAI
    ifstream in("in.txt");
    cin.rdbuf(in.rdbuf());
    //  ofstream out("main.out");
    //  cout.rdbuf(out.rdbuf());
#endif
    auto get_max = [](int l, int r, int s) {
        int cand1, cand2;
        if (s % 2 == 0) {
            cand1 = cand2 = s / 2;
        } else {
            cand1 = (s - 1) / 2;
            cand2 = cand1 + 1;
        }
        if (l <= cand1 && cand1 <= r) {
            return 1ll * cand1 * (s - cand1);
        }
        if (l <= cand2 && cand2 <= r) {
            return 1ll * cand2 * (s - cand2);
        }
        return max(1ll * l * (s - l), 1ll * r * (s - r));
    };
    auto check = [&get_max](int a, int b, int n) {
        ll max_v = 0;
        vector<pii> x, y;
        if (a > n) x.eb(1, n);
        else {
            if (a != 1) x.eb(1, a - 1);
            x.eb(a + 1, n + 1);
        }
        if (b > n) y.eb(1, n);
        else {
            if (b != 1) y.eb(1, b - 1);
            y.eb(b + 1, n + 1);
        }
        int i = 0, j = SZ(y) - 1;
        while (i < SZ(x)) {
            int t = min(x[i].second - x[i].first + 1, y[j].second - y[j].first + 1);
            chkmax(max_v, get_max(x[i].first, x[i].first + t - 1, x[i].first + y[j].second));
            x[i].first += t;
            y[j].second -= t;
            if (x[i].first > x[i].second) ++i;
            if (y[j].first > y[j].second) --j;
        }
        return max_v;
    };
    int q;
    scan(q);
    rep (q) {
        int a, b;
        scan(a, b);
        int l = 1, r = 2 * (max(a, b) - 1);
        while (l <= r) {
            int mid = get_mid(l, r);
            if (check(a, b, mid) < 1ll * a * b) {
                l = mid + 1;
            } else {
                r = mid - 1;
            }
        }
        println(r);
    }
#if defined LOCAL && !defined DUIPAI
    cout << "Time elapsed: " << 1.0 * clock() / CLOCKS_PER_SEC << " s.\n";
#endif
    return 0;
}
posted @ 2020-01-03 18:50  Pat  阅读(366)  评论(0编辑  收藏  举报