AtCoder Beginner Contest 133 解题报告 (A ~ E)
比赛地址:AtCoder Beginner Contest 133
A - T or T
题目大意:\(n\) 个同学一起去旅行,坐火车的话每人要 \(a\) 元,做出租车的话总共要 \(b\) 元,问最少花费多少元?
解题思路:直接计算取最小值即可
#include <cstdio>
#include <algorithm>
int main() {
int n, a, b;
scanf("%d%d%d", &n, &a, &b);
printf("%d", std::min(a * n, b));
return 0;
}
B - Good Distance
题目大意:\(D\) 维空间内有 \(n\) 个点,问有多少点对,两个点之间的距离为整数, \(D,n\le 10\) 。
解题思路:由于数据范围很小,暴力计算判断即可
#include <cstdio>
#include <cmath>
int n, d, ans;
int point[11][11];
bool get_dist(int x, int y) {
int tmp = 0;
for (int i = 1; i <= d; ++i) {
tmp += pow(point[x][i] - point[y][i], 2);
}
if (int(sqrt(tmp)) == sqrt(tmp))
return true;
else
return false;
}
int main() {
scanf("%d%d", &n, &d);
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= d; ++j) {
scanf("%d", &point[i][j]);
}
}
for (int i = 1; i <= n; ++i) {
for (int j = i + 1; j <= n; ++j) {
ans += get_dist(i, j);
}
}
printf("%d", ans);
return 0;
}
C - Remainder Minimization 2019
题目大意:在区间 \([L,R]\) 内寻找两个不同的整数 \(i\) 和 \(j\) ,使得 \(j \times j\ Mod\ 2019\) 最小,输出这个最小值。
解题思路:如果 \(R - L \ge 2019\) ,那么在区间 \([L,R]\) 内一定存在一个数 \(n\) 使得 \(n\ Mod\ 2019 = 0\) ,此时答案一定为 \(0\) ,所以当 \(R - L\ge 2019\) 时,我们直接让 \(R = L + 2019\) 即可,此操作过后,数据范围大大减小,就可以在区间 \([L,R]\) 内暴力枚举了。
值得注意的是:不能想当然地认为找到模 \(2019\) 最小的两个数相乘就可以得到答案了,因为这两个数相乘得到的模数可能是 \(2018\) ,而且存在另外两个数相乘模 \(2019\) 的值更小。
#include <cstdio>
#include <algorithm>
int left, right, ans;
int cnt[2019], min[2];
int main() {
scanf("%d%d", &left, &right);
right = std::min(right, left + 2019);
ans = 2019;
for (int i = left; i <= right ; ++i) {
for (int j = i + 1; j <= right; ++j) {
ans = std::min(ans, (i % 2019) * (j % 2019) % 2019);
}
}
printf("%d", ans);
return 0;
}
D - Rain Flows into Dams
题目大意:有 \(n\) (\(n\) 为奇数)座山围成一圈,相邻两座山之间有水坝,如果山上下了 \(2x\) 的雨,那么与这座山相邻的两座水坝各会积累 \(x\) 的水量,现在给出每个水坝的最终积水量,求每座山上下了多少的雨。
解题思路:我们不妨设编号为 \(i\) 的山上下了 \(x_i\) 的雨,编号为 \(i\) 的大坝积水量为 \(A_i\) ,那么根据题意我们可以得到如下的方程组:
由于 \(n\) 为奇数,我们将所有奇数行的方程相加就可以得到 \(\sum\limits_{i = 1}^{n}x_i + x_1\) 的结果,而 \(\sum\limits_{i = 1}^{n}x_i = \sum\limits_{i = 1}^nA_i\) ,由此我们就可以算出 \(x_1\) ,由此代入方程组,就可以解出所有的 \(x\) 了。
#include <cstdio>
const int MAXN = 1e5 + 5;
int n, cnt, sum;
int arr[MAXN], ans[MAXN];
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
scanf("%d", &arr[i]);
sum += arr[i];
if (i & 1)
cnt += (arr[i] << 1);
}
ans[1] = cnt - sum;
for (int i = 1; i < n; ++i) {
ans[i + 1] = (arr[i] << 1) - ans[i];
}
for (int i = 1; i <= n; ++i) {
printf("%d ", ans[i]);
}
return 0;
}
E - Virus Tree 2
题目大意:给你有一棵有 \(n\) 个节点的树和 \(k\) 种颜色,现在要将这棵树的每一个节点都染上色,要求距离小于等于 \(2\) 的点不能染上相同的颜色吗,求染色方案数对 \(1000000007\) 取模。
解题思路:我们考虑解出每一个节点有多少种染色方案,我们令 \(dp[i]\) 表示第 \(i\) 个节点有多少个染色的方案,那么我们如果从根节点开始 DP ,先考虑每一条链的情况,记当前节点为 \(nv\) ,如果是根节点,那么 \(dp[nv] = k\) ,如果是根节点的儿子节点,那么 \(dp[nv] = k - 1\) ,如果是根节点的孙子节点或者更深的节点,那么他不仅会受到父节点的影响,也会受到祖父节点的影响,所以 \(dp[nv] = k - 2\) ,这样我们就可以处理出一条链上所有的节点的染色方案数。现在考虑一般情况,我们发现一个节点的兄弟也会对他产生影响,因此在处理一个节点的儿子时,它的儿子的 \(dp\) 值是不断递减的,但是这并不会影响它的某一个孙子的答案,因为它的孙子不会受到儿子的其他兄弟的影响(树上距离大于 \(2\) ),这样子我们就处理出了一般情况下所有节点的染色方案数,将他们相乘就可以得到答案了。
#include <cstdio>
#include <vector>
typedef long long int ll;
const int MAXN = 1e5 + 5;
const int mod = 1e9 + 7;
int n, k, u, v;
ll ans;
ll dp[MAXN];
std::vector<int> con[MAXN];
void ins(int start, int end) {
con[start].push_back(end);
}
void get_dp(int nv, int fa) {
int tmp = (nv == 1) ? (k - 1) : (k - 2);
for (int i = 0; i < con[nv].size(); ++i) {
if (con[nv][i] == fa)
continue;
else {
dp[con[nv][i]] = tmp;
get_dp(con[nv][i], nv);
--tmp;
}
}
}
int main() {
scanf("%d%d", &n, &k);
for (int i = 1; i < n; ++i) {
scanf("%d%d", &u, &v);
ins(u, v);
ins(v, u);
}
dp[1] = k;
get_dp(1, 0);
ans = 1;
for (int i = 1; i <= n; ++i) {
ans = ans * dp[i] % mod;
}
printf("%lld", ans);
return 0;
}