AtCoder Beginner Contest 246 - 题解
A - Four Points
题意简述
- 平面中有一个长方形,给定它三个顶点的坐标 \((x_1, y_1), (x_2, y_2), (x_3, y_3)\),输出它第四个顶点的坐标。
- \(-100 \leq x_i, y_i \leq 100\)。
解题思路
题目要求我们输出第四个顶点的坐标。长方形顶点的坐标有什么性质呢?
观察上图,我们发现顶点 A、B 的横坐标相同,C、D 的横坐标也相同,而 B、C 的纵坐标相同,A、D 的纵坐标相同。
所以四个顶点中,有两组点横坐标相同,又有两组点纵坐标相同。
那么解法也呼之欲出了:我们找出给定的三个点内哪个点的横坐标不与另外两个点相同,它的横坐标必定与第四个点相同,我们就可以找出第四个点的横坐标了。纵坐标同样处理即可。
代码
#include <bits/stdc++.h>
using namespace std;
inline int read() {
int x = 0, f = 1; char ch = getchar();
while (!isdigit(ch)) f = (ch == '-' ? -1 : f), ch = getchar();
while (isdigit(ch)) x = (x<<3) + (x<<1) + ch - '0', ch = getchar();
return x*f;
}
int x[5], y[5];
int main() {
x[1] = read(), y[1] = read();
x[2] = read(), y[2] = read();
x[3] = read(), y[3] = read();
if (x[1] == x[2]) x[4] = x[3];
else if (x[2] == x[3]) x[4] = x[1];
else x[4] = x[2];
if (y[1] == y[2]) y[4] = y[3];
else if (y[2] == y[3]) y[4] = y[1];
else y[4] = y[2];
printf("%d %d", x[4], y[4]);
}
B - Get Closer
题意简述
- 给定坐标系内的一个点 \((a, b)\)。现已知有另一个点从 \((0, 0)\) 出发,朝着 \((a, b)\) 的方向移动一个单位长度,求此时它的坐标。
- \(0 \leq a, b \leq 1000\),\((a, b) \neq (0, 0)\)。
解题思路
画出图观察观察吧。
我们设 A 为原点,B 为给定点,P 为移动后到达的点,过 P,B 做 x 轴垂线,交 x 轴于 M,N。
我们可以通过勾股定理求出 \(AB\) 的长。
接下来,容易发现 \(AP : AB = AM : AN\),而 \(AN\) 是已知的,\(AB\) 是可求的,\(AP\) 就等于 \(1\),那么我们就可以算出 \(AM\) 的值啦!
也就是说我们得到了 P 点的横坐标,用同样的方法算出纵坐标即可。
#include <bits/stdc++.h>
using namespace std;
inline int read() {
int x = 0, f = 1; char ch = getchar();
while (!isdigit(ch)) f = (ch == '-' ? -1 : f), ch = getchar();
while (isdigit(ch)) x = (x<<3) + (x<<1) + ch - '0', ch = getchar();
return x*f;
}
int a, b;
double c;
int main() {
a = read(), b = read();
c = sqrt(a*a*1.0 + b*b*1.0);
printf("%lf %lf", a*1.0/c, b*1.0/c);
}
C - Coupon
题意简述
- 给定长度为 \(n\) 的序列 \(a_n\) 与两个数 \(k, x\),你可以 \(k\) 次操作。
- 对于每一次操作,你可以指定一个数 \(i\),并让 \(a_i = \min(a_i - x, 0)\)。
- 在所有的 \(k\) 次操作后,计算此时序列里数的和。你需要输出这个和的最小值。
- \(1 \leq n \leq 2 \times 10^5\),\(1 \leq k, x \leq 10^9\),\(1 \leq a_i \leq 10^9\)。
解题思路
观察题目,我们发现每次操作至多能使序列的和降低 \(x\)。那么我们就先把所有能使和降低 \(x\) 的操作做完吧。
如果操作过程中发现 \(k\) 次操作已经用光了,那么我们就直接输出此时序列的和。
如果操作完了 \(k\) 次操作还没用光,应该怎么办呢?
容易发现此时序列中每个数都小于 \(x\),那么这时候下标的 \(i\) 的数最多只能让和降低 \(a_i\) 次了。
问题也就迎刃而解了:我们先把能将和降低 \(x\) 的操作做完,如果 \(k\) 次操作还没用光,我们就将此时的 \(a\) 序列从大到小排序,依次对每一个数进行操作即可。
代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
inline int read() {
int x = 0, f = 1; char ch = getchar();
while (!isdigit(ch)) f = (ch == '-' ? -1 : f), ch = getchar();
while (isdigit(ch)) x = (x<<3) + (x<<1) + ch - '0', ch = getchar();
return x*f;
}
const int L = 2e5 + 5;
int n, k, x, cnt, sum, a[L];
signed main() {
n = read(), k = read(), x = read();
for (int i = 1; i <= n; i++)
a[i] = read(), sum += a[i], cnt += a[i]/x, a[i] = a[i]%x;
if (cnt >= k) {
printf("%lld", sum-x*k);
return 0;
}
k -= cnt, sum -= cnt*x;
sort(a+1, a+1+n, greater<int>());
for (int i = 1; i <= min(k, n); i++)
sum -= a[i];
printf("%lld", sum);
return 0;
}
D - 2-variable Function
题意简述
- 给定一个整数 \(n\),试求一个最小的 \(x\),满足:
- \(x \geq n\)。
- 存在非负整数对 \((a, b)\) 使得 \(x = a^3 + a^2b + ab^2 + b^3\)。
- \(0 \leq n \leq 10^{18}\)。
解题思路
考虑对原式化简。
我们知道,\((a+b)^3 = a^3 + 3a^2b + 3ab^2 + b^3\),所以
很显然我们有 \((a+b)^3 \geq x\)。
那么我们考虑枚举 \((a+b)\):
- 如果 \(a+b < n\),直接
continue
; - 如果 \(a+b \geq n\),则枚举 \(a\),并对于每一个 \(a\) 计算出 \((a+b)^3 - 2ab(a+b)\) 的值,从而找出满足条件的最小的 \(x\)。
但我们总不能一直枚举下去吧。什么时候 break
呢?
考虑到 \(4ab \leq (a+b)^2\),所以 \((a+b)^3 - 2ab(a+b) \geq \frac{1}{2}(a+b)^3\)。
我们设 \(k\) 是使 \(k^3 \geq n\) 的最小正整数,如果 \((a+b)^3 \geq 2 \cdot k^3\),此时 \((a+b)^3 - 2ab(a+b)\) 一定不优于 \((0+k)^3 - 2 \times 0 \times k \times (0+k)\),那么我们果断 break
就好啦。
这样的做法大约是 \(O((2n)^{\frac{2}{3}})\),显然会超时。怎么优化呢?
我们发现如果固定 \(a+b\),不妨设 \(a \leq b\),此时 \(ab\) 会随 \(a\) 增大而增大,所以 \((a+b)^3-2ab(a+b)\) 会随 \(a\) 增大而减小。
也就是说,\((a+b)^3-2ab(a+b)\) 具有单调性!那么我们二分即可。
代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
inline int read() {
int x = 0, f = 1; char ch = getchar();
while (!isdigit(ch)) f = (ch == '-' ? -1 : f), ch = getchar();
while (isdigit(ch)) x = (x<<3) + (x<<1) + ch - '0', ch = getchar();
return x*f;
}
int n, l, r, mid, num, ans;
signed main() {
n = read();
while (num*num*num < n)
num++;
ans = 2e18;
for (int x = num; x*x*x <= 2*num*num*num; x++){
l = 0, r = x/2;
while (l < r) {
mid = ((l+r+1)/2);
if (n <= x*x*x-2*mid*(x-mid)*x)
l = mid;
else r = mid-1;
}
ans = min(ans, x*x*x-2*l*(x-l)*x);
}
printf("%lld", ans);
return 0;
}