AtCoder Beginner Contest 065
A
题意:
tk 如果吃不超过保质期 \(X\) 天的食品不会得胃病,吃没有超过保质期的视频会感到美味,否则会得胃病。
他今天买了一袋食物,还剩 \(A\) 天到保质期,打算 \(B\) 天后吃掉。
询问他吃下这袋食物时感到的状态:delicious、sate、dangerous。
题解:
还剩下 \(A - B\) 天到保质期,若 \(A - B \geq 0\) ,则没有吃过期食品。
否则超过了保质期 \(B - A\) 天,若 \(B - A \geq X\) ,则不会生病。
否则会生病。
view
int X, A, B; std::cin >> X >> A >> B;
if (A - B >= 0) std::cout << "delicious\n";
else if (B - A <= X) std::cout << "safe\n";
else std::cout << "dangerous\n";
B
题意:
有 \(N\) 个灯,每个灯有一个按钮。若按下第 \(i(1 \leq N)\) 栈灯的按钮,则这栈灯会熄灭,第 \(a_i(1 \leq a_i \leq N)\) 栈灯会亮起。
开始时第 \(1\) 栈灯是亮的,不保证不存在 \(i \neq a_i\) ,询问第 \(2\) 栈灯亮起,最少需要按多少次按钮。或则回答不可能。
题解:
由鸽巢原理,一定存在混循环或纯循环,且环最大为 \(N\) 。
如果第 \(2\) 栈灯可以亮起,但 \(1, 2\) 属于一个混循环或纯循环。
否则 \(1, 2\) 不在一个循环内,不可能让第 \(2\) 栈灯亮起。
遍历包含 \(1\) 的循环即可。
view
int n; std::cin >> n;
std::vector<int> a(n + 1), vis(n + 1);
for (int i = 1; i <= n; i++) std::cin >> a[i];
int x = 1; vis[x] = 1;
int cnt = 0;
while (true) {
cnt++;
vis[x = a[x]] = 1;
if (x == 2 || vis[a[x]]) break;
}
if (x != 2) cnt = -1;
std::cout << cnt << "\n";
时间复杂度 \(O(N)\) 。
C
题意:
有 \(N\) 只狗和 \(M\) 只猫,需要排列成一行,要求没有任意相邻两只动物种类一样,询问总共有多少种排列方案。每只猫和狗不一样。
\(1 \leq N, M \leq 10^{5}\)
题解:
只能交替排列
若 \(|N - M| > 1\) 不存在方案。
若 \(N = M\) 。第一个位置有 \(2\) 种选择。然后剩下的位置可以被确定。同种类位置计算全排列。方案数贡献为乘法原理 \(2 \times N! \times M!\) 。
若 \(|N - M| = 1\) ,不关心第一个位置,因为只有一种方案。同种类的位置计算全排列。方案数贡献为乘法原理: \(N! \times M!\) 。
view
fac[0] = 1;
for (int i = 1; i < MAXN; i++) fac[i] = 1LL * fac[i - 1] * i % MOD;
int N, M; std::cin >> N >> M;
if (abs(N - M) > 1) std::cout << 0 << "\n";
else if (N == M) std::cout << 2LL * fac[N] * fac[M] % MOD << "\n";
else std::cout << 1LL * fac[N] * fac[M] % MOD << "\n";
D
题意:
有 \(N\) 个城镇在一个二维平面上,第 \(i(1 \leq i \leq N)\) 个城镇的坐标为 \()x_i ,y_i)\) 。
任意两个位置 \((a, b), (c, d)\) 之间修一条路的代价为 \(min(|a - b|, |c - d|)\) 。
询问让所有城镇成为连通图,需要支付的最少修路代价。输出答案 \(\mod 1E9 + 7\) 。
\(2 \leq N \leq 10^{5}, 0 \leq x_i, y_i \leq 10^{9}\)
题解:
笑死,一眼不会。
官方思路:
我们考虑最小生成树。任意两点 \(i, j\) 的坐标为 \((x_{i}, y_{i}), (x_{j}, y_{j})\) ,则 \(i, j\) 之间可以存在一条边权为 \(min(|x_i - x_j|, |y_i - y_j|)\) 的边。
如果把所有边都建出来,则边的数量为 \(\binom{N}{2} = \frac{N(N + 1)}{2}\) 。显然不可行。
单独考虑每一维。
将点按 \(X\) 轴排序,得到点的编号为 \(a_{p_1}, a_{p_2}, a_{p_3}, \cdots, a_{p_N}\) 。因为显然其他点连边是没有意义的。
\(Y\) 轴同理。
实际上有意义的边只有 \(2(N - 1)\) 条,有向边有 \(M = 4(N - 1)\) 条。
可以跑 Kruskal ,时间复杂度 \(O(M \log M)\) 。时间复杂度贡献依赖于边的排序。
view
const int MOD = 1E9 + 7;
const int MAXM = 4E5+10;
int fa[MAXM];
int find(int x) { return x == fa[x] ? x : fa[x] = find(fa[x]); }
struct Edge {
int u, v, w;
bool operator < (Edge &e) const {
return w < e.w;
}
} E[MAXM];
int etot;
void solve() {
etot = 0;
for (int i = 0; i < MAXM; i++) fa[i] = i;
int N; std::cin >> N;
std::vector<std::array<int, 3> > a(N + 1);
for (int i = 1; i <= N; i++) {
int x, y; std::cin >> x >> y;
a[i] = {x, y, i};
}
std::sort(a.begin() + 1, a.end(), [&](std::array<int, 3> p1, std::array<int, 3> p2){
return p1[0] < p2[0];
});
for (int i = 2; i <= N; i++) {
int u = a[i - 1][2];
int v = a[i][2];
int w = a[i][0] - a[i - 1][0];
E[++etot] = {u, v, w};
E[++etot] = {v, u, w};
}
std::sort(a.begin() + 1, a.end(), [&](std::array<int, 3> p1, std::array<int, 3> p2){
return p1[1] < p2[1];
});
for (int i = 2; i <= N; i++) {
int u = a[i - 1][2];
int v = a[i][2];
int w = a[i][1] - a[i - 1][1];
E[++etot] = {u, v, w};
E[++etot] = {v, u, w};
}
std::sort(E + 1, E + etot + 1);
int ans = 0;
for (int i = 1; i <= etot; i++) {
int u = E[i].u, v = E[i].v, w = E[i].w;
if (find(u) == find(v)) continue;
fa[find(u)] = find(v);
ans = (1LL * ans + w) % MOD;
}
std::cout << ans << "\n";
}
浙公网安备 33010602011771号