出题人题解。
E 黄牛の争
数学模型
考虑优化 Special Judge 中 win
函数部分的暴力,首先记
-
\(\alpha=\left\lceil\dfrac{B}{a}\right\rceil\) 表示 \(\tt A\) 击败 \(\tt B\) 所需回合数;
-
\(\beta=\left\lceil\dfrac{A}{b}\right\rceil\) 表示 \(\tt B\) 击败 \(\tt A\) 所需回合数。
那么:
-
如果 \(\tt B\) 是先手,仅需 \(\beta\le\alpha\) 即可击败 \(\tt A\);
-
如果 \(\tt B\) 是后手,则需 \(\beta<\alpha\) 才能击败 \(\tt A\)。
据此,可以得到 \(\tt B\) 击败 \(\tt A\) 的条件:
那么 \(O(1)\) 的判定胜负的代码:
bool win(int a, int b, int c, int d){
return (b + c - 1) / c + (c < a) <= (a + d - 1) / a;
}
下面开始分析每个子任务的得分方式。
Subtask 1
\(c,C\) 枚举上限 \((M-1)^2\),考虑优化:
-
\(\texttt{C}\) 只需要能击败 \(\texttt{B}\),因此 \(c\) 的枚举上限应为 \(\max(1+b,B)\)。
-
\(\texttt{C}\) 必须输给 \(\texttt{A}\),因此 \(c\) 的枚举上限应为 \(\max(a,A)-1\)。
Subtask 2
固定 \(c\) 之后,\(C\) 越大 \(\tt C\) 越强,\(C\) 越小 \(\tt C\) 越弱。
所以 \(C\) 的合法取值一定在一个区间 \([l,r]\) 上,可以考虑二分 \(C\)。
Subtask 3, 5
留给一些正确性可能看起来很对的奇怪做法。(比如某不知名 \(O(\sqrt[3]M\log M)\) 的做法)
Subtask 4
那么这部分分显然是希望我们给出一个 \(O(M)\) 的线性做法。
考虑如果我们确定了 \(c\),继续推一推式子(其实也就是挪一挪系数),得到:
据此就可以 \(O(1)\) 求出 \(C\) 的值。
Subtask 6
考虑一种更快的枚举 \(c\) 的方式。
由于 \(c\) 作为分母,它的取值只影响
的取值,我们可以整除分块枚举 \(c\) 的取值。
一种上取整转下取整的办法:
另外,朴素的整除分块只枚举到
但是我们的 \(c\) 应当一直到
处都可能具有贡献。
所以我们考虑根据题意分成两段枚举,第一段使用朴素的整除分块。
第二段枚举 \(\min(A,B)\sim\max(A,B)\) 之间的答案贡献部分即可。
std
//E1
#include<cstdio>
int init(){
char c = getchar();
int x = 0, f = 1;
for (; c < '0' || c > '9'; c = getchar())
if (c == '-') f = -1;
for (; c >= '0' && c <= '9'; c = getchar())
x = (x << 1) + (x << 3) + (c ^ 48);
return x * f;
}
void print(int x){
if (x < 0) x = -x, putchar('-');
if (x > 9) print(x / 10);
putchar(x % 10 + '0');
}
bool win(int a, int b, int c, int d){
return (b + c - 1) / c + (c < a) <= (a + d - 1) / a;
}
int mn(int a, int b){
return a < b ? a : b;
}
int mx(int a, int b){
return a > b ? a : b;
}
int main(){
int q = init();
while (q--) {
int a = init(), A = init(), b = init(), B = init();
if (win(b, B, a, A)) {
puts("-1 -1");
continue;
}
bool flag = 1;
for(int x = 1; x <= 100; ++x)
for(int y = 1; y <= 100 && flag; ++y)
if (x != a && x != b && win(b, B, x, y) && win(x, y, a, A))
flag = 0, print(x), putchar(' '), print(y), putchar('\n');
if(flag)
print(-1), putchar(' '), print(-1), putchar('\n');
}
}
//E2
#include<cstdio>
#define int long long
int init(){
char c = getchar();
int x = 0, f = 1;
for (; c < '0' || c > '9'; c = getchar())
if (c == '-') f = -1;
for (; c >= '0' && c <= '9'; c = getchar())
x = (x << 1) + (x << 3) + (c ^ 48);
return x * f;
}
void print(int x){
if (x < 0) x = -x, putchar('-');
if (x > 9) print(x / 10);
putchar(x % 10 + '0');
}
bool win(int a, int b, int c, int d){
return (b + c - 1) / c + (c < a) <= (a + d - 1) / a;
}
int mn(int a, int b){
return a < b ? a : b;
}
int mx(int a, int b){
return a > b ? a : b;
}
signed main(){
int q = init();
while (q--) {
int a = init(), A = init(), b = init(), B = init();
int MAX = mn(mx(1 + b, B), mx(a, A) - 1);
int M = mx(mx(a, A), mx(b, B));
if (!win(a, A, b, B) || (B > A && b > a)) {
puts("-1 -1");
continue;
}
bool flag=1;
for (int c = 1; c <= MAX; ++c) {
if (c == a || c == b)
continue;
int l = 1, r = (M - 1) * (M - 1), ans = -1;
if (c > a) r = A - 1;
if (c < b) l = B + 1;
while (l <= r) {
int mid = l + r >> 1;
if (!win(c, mid, a, A))
r = mid - 1;
else if (!win(b, B, c, mid))
l = mid + 1;
else {
ans = mid;
break;
}
}
if (ans != -1) {
print(c), putchar(' '), print(ans), putchar('\n');
flag = 0;
break;
}
}
if (flag)
puts("-1 -1");
}
}
//E3
#include<cstdio>
#define int long long
int init(){
char c = getchar();
int x = 0, f = 1;
for (; c < '0' || c > '9'; c = getchar())
if (c == '-') f = -1;
for (; c >= '0' && c <= '9'; c = getchar())
x = (x << 1) + (x << 3) + (c ^ 48);
return x * f;
}
void print(int x){
if (x < 0) x = -x, putchar('-');
if (x > 9) print(x / 10);
putchar(x % 10 + '0');
}
bool win(int a, int b, int c, int d){
return (b + c - 1) / c + (c < a) <= (a + d - 1) / a;
}
int a, A, b, B;
int ok(int c){
if (c == a || c == b)
return 0;
int l = b * (B / c + (c < b)),
r = (A / c - (a < c)) * a + a - 1;
if (l > r)
return 0;
return l + 1;
}
int mn(int a, int b){
return a < b ? a : b;
}
int mx(int a, int b){
return a > b ? a : b;
}
signed main(){
int t = init();
for (int i = 1; i <= t; ++i) {
a = init(), A = init(), b = init(), B = init();
if (!win(a, A, b, B) || (B > A && b > a)) {
puts("-1 -1");
continue;
}
--A, --B;
int c = 0, C;
if (!ok(a + 1) && !ok(b + 1)) {
for (int i = 1; i <= mx(A, B); ++i)
if (ok(i)) {
c = i, C = ok(i);
break;
}
}
else {
if (ok(a + 1))
c = a + 1, C = ok(a + 1);
if (ok(b + 1))
c = b + 1, C = ok(b + 1);
}
if (c)
print(c), putchar(' '), print(C), putchar('\n');
else
puts("-1 -1");
}
}
//E4
#include<cstdio>
#define int long long
int init(){
char c = getchar();
int x = 0, f = 1;
for (; c < '0' || c > '9'; c = getchar())
if (c == '-') f = -1;
for (; c >= '0' && c <= '9'; c = getchar())
x = (x << 1) + (x << 3) + (c ^ 48);
return x * f;
}
void print(int x){
if (x < 0) x = -x, putchar('-');
if (x > 9) print(x / 10);
putchar(x % 10 + '0');
}
bool win(int a, int b, int c, int d){
return (b + c - 1) / c + (c < a) <= (a + d - 1) / a;
}
int a, A, b, B;
int ok(int c){
if (c == a || c == b)
return 0;
int l = b * (B / c + (c < b)),
r = (A / c - (a < c)) * a + a - 1;
if (l > r)
return 0;
return l + 1;
}
int mn(int a, int b){
return a < b ? a : b;
}
int mx(int a, int b){
return a > b ? a : b;
}
signed main(){
int t = init();
for (int i = 1; i <= t; ++i) {
a = init(), A = init(), b = init(), B = init();
if (!win(a, A, b, B) || (B > A && b > a)) {
puts("-1 -1");
continue;
}
--A, --B;
int c = 0, C;
if (!ok(a + 1) && !ok(b + 1)) {
for (int i = 1; i <= A && i <= B; i = mn(A / (A / i), B / (B / i)) + 1)
if (ok(i)) {
c = i, C = ok(i);
break;
}
for (int i = mn(A, B) + 1; i <= mx(A, B); i = mx(A, B) / (mx(A, B) / i) + 1)
if (ok(i)) {
c = i, C = ok(i);
break;
}
}
else {
if (ok(a + 1))
c = a + 1, C = ok(a + 1);
if (ok(b + 1))
c = b + 1, C = ok(b + 1);
}
if (c)
print(c), putchar(' '), print(C), putchar('\n');
else
puts("-1 -1");
}
}