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";
}
posted @ 2024-06-03 02:33  03Goose  阅读(18)  评论(0)    收藏  举报