标签:turn time ++i ret swap cond sed 整数 最大值
二分答案。
首先确定答案的上界。
不妨设 $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);
if (a > b) swap(a, b);
int l = 1, r = 2 * (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;
}
标签:turn time ++i ret swap cond sed 整数 最大值
原文地址:https://www.cnblogs.com/Patt/p/12146362.html