2025春训第二十场

StarStar
5 min read

第一场打完觉的非常水,于是第二场立刻就被真实了,已老实求放过😇😇😇。

A. 舞蹈机器人

熟悉的情况,熟悉的打表(

打表

#include <iostream>
#include <set>

using namespace std;

const int dx[] = {1, 0, -1, 0}, dy[] = {0, 1, 0, -1};
set<pair<int, int>> s;

void dfs(int x, int y, int dep, int lim, bool f) {
    if (dep == lim) {
        s.insert({x, y});
        return;
    }
    for (int i = f; i < 4; i += 2) {
        dfs(x + dx[i], y + dy[i], dep + 1, lim, f ^ 1);
    }
}

int main() {
    int n = 50;
    for (int i = 1; i <= 20; ++i) {
        s.clear();
        dfs(0, 0, 0, i, 0);
        dfs(0, 0, 0, i, 1);
        cout << s.size() << endl;
    }
    cout << endl;
    return 0;
}

/*
4 12 24 40 60 84 112
4  8 12 16 20 24 28
1  2  3  4  5  6  7

4 * n * (n - 1) / 2
*/

AC 代码

n = int(input())
if n & 1:
    n = (n + 1) // 2
    print(n * (n + 1) * 2)
else:
    n = n // 2 + 1
    print(n * n)

B. 狗是啥呀

这道比较简单一点,找一个单次伤害最大的作为最后一击,前面的找一个净伤害最高的一直打即可。

#include <iostream>
#include <climits>

using namespace std;

int main() {
    int n;
    long long x, mxd = LLONG_MIN, mxk = 0;
    cin >> n >> x;
    for (int i = 1; i <= n; ++i) {
        long long d, h;
        cin >> d >> h;
        mxk = max(mxk, d);
        mxd = max(mxd, d - h);
    }
    if (x <= mxk) cout << 1 << endl;
    else if (mxd <= 0) cout << -1 << endl;
    else cout << 1 + (x - mxk + mxd - 1) / mxd << endl;
    return 0;
}

C. 枢纽

看到这道题我直接就想到了点双联通分量缩点,于是写缩点写挂了(-14)……

事实上只有一组查询,完全可以暴力解决,从 a 出发搜索,经过 b 就直接 return 把搜到的点打上标记;同理从 b 出发打标记。如此,所有的点被分成三大区。

  • 如果一个点两个标记都存在,表明他可以只经过 a 或者 b 到其他任何点,否则不能;

  • 如果只有 a 或者 b 的标记,这两块点之间必须经过 a 和 b 才行。

所以答案是只被 a 标记的点个数 * 只被 b 标记的点个数。

优雅的做法

#include <iostream>
#include <cstring>

using namespace std;

const int N = 200010, M = 1000010;

int head[N], ver[M], ne[M], tot = 1;
bool vis[N][2];

void add(int x, int y) {
    ver[++tot] = y;
    ne[tot] = head[x];
    head[x] = tot;
}

void dfs(int x, bool f, int t) {
    if (vis[x][f] || x == t) return;
    vis[x][f] = true;
    for (int i = head[x]; i; i = ne[i]) {
        int y = ver[i];
        dfs(y, f, t);
    }
}

int main() {
    int n, m, a, b;
    scanf("%d%d%d%d", &n, &m, &a, &b);
    for (int i = 1; i <= m; ++i) {
        int x, y;
        scanf("%d%d", &x, &y);
        add(x, y), add(y, x);
    }
    dfs(a, 0, b);
    dfs(b, 1, a);
    int t1 = 0, t2 = 0;
    for (int i = 1; i <= n; ++i) {
        if (vis[i][0] && !vis[i][1]) t1++;
        else if (vis[i][1] && !vis[i][0]) t2++;
    }
    t1--, t2--;
    printf("%lld\n", (long long)t1 * t2);
    return 0;
}

丑陋的做法(缩点)

#include <iostream>
#include <set>
#include <vector>
#include <stack>

using namespace std;

const int N = 200010, M = 1000010;

set<pair<int, int>> s;
int head[N], ver[M], ne[M], tot = 1;
int dfn[N], low[N], t;
int bcc_id[N], bcc_siz[N * 2], bcc_cnt;
bool cut[N];
vector<int> ed[N * 2], bcc[N];
stack<int> st;
int fa[N * 2], dep[N * 2];

void add(int x, int y) {
    ver[++tot] = y;
    ne[tot] = head[x];
    head[x] = tot;
}

void tarjan(int x, int from) {
    dfn[x] = low[x] = ++t;
    st.push(x);
    int cnt = 0;
    for (int i = head[x]; i; i = ne[i]) {
        if (i == (from ^ 1)) continue;
        int y = ver[i];
        if (!dfn[y]) {
            tarjan(y, i);
            low[x] = min(low[x], low[y]);
            if (dfn[x] <= low[y]) {
                cnt++;
                if (x != 1 || cnt > 1) cut[x] = true;
                bcc_cnt++;
                int tp;
                do {
                    tp = st.top();
                    st.pop();
                    bcc_id[tp] = bcc_cnt;
                    bcc_siz[bcc_cnt]++;
                    bcc[bcc_cnt].push_back(tp);
                } while (tp != y);
                bcc_siz[bcc_cnt]++;
                bcc[bcc_cnt].push_back(x);
            }
        }
        else low[x] = min(low[x], dfn[y]);
    }
}

void dfs(int x) {
    for (int y : ed[x]) {
        if (y == fa[x]) continue;
        fa[y] = x;
        dep[y] = dep[x] + 1;
        dfs(y);
        bcc_siz[x] += bcc_siz[y];
    }
}

int check(int x, int y) {
    while (x) {
        if (fa[x] == y) return x;
        x = fa[x];
    }
    return 0;
}

int main() {
    int n, m, a, b;
    scanf("%d%d%d%d", &n, &m, &a, &b);
    for (int i = 1; i <= m; ++i) {
        int x, y;
        scanf("%d%d", &x, &y);
        if (x == y) continue;
        if (x > y) swap(x, y);
        if (s.find({x, y}) != s.end()) continue;
        s.insert({x, y});
        add(x, y), add(y, x);
    }
    tarjan(1, 0);
    for (int i = 1; i <= n; ++i) {
        if (cut[i]) {
            bcc_id[i] = ++bcc_cnt;
            bcc_siz[bcc_cnt] = 1;
        }
    }
    for (int i = 1; i <= bcc_cnt; ++i) {
        if (!bcc[i].empty()) {
            for (int x : bcc[i]) {
                if (cut[x]) {
                    bcc_siz[i]--;
                    ed[bcc_id[x]].push_back(i);
                    ed[i].push_back(bcc_id[x]);
                }
            }
        }
        else break;
    }
    if (!cut[a] || !cut[b]) {
        printf("0\n");
    }
    else {
        dfs(1);
        int x = bcc_id[a], y = bcc_id[b];
        if (dep[x] > dep[y]) swap(x, y);
        int f = check(y, x);
        if (f) { // x 是 y 的祖先
            printf("%lld\n", (long long)(n - bcc_siz[f] - 1) * (bcc_siz[y] - 1));
        }
        else {
            printf("%lld\n", (long long)(bcc_siz[x] - 1) * (bcc_siz[y] - 1));
        }
    }
    return 0;
}

D. 魔法药水

这是一道 dp 题。

n ≤ 100,意味着每秒递增的魔力值只会是 1 ~ 100 之间的整数。枚举选择的瓶数 t,维护一个状态 \(f_{i, j, k}\) 表示前 i 瓶药水选了 j 瓶,魔力值之和对 t 的余数为 k 时魔力值的最大值。对于每一个 t,如果存在状态 \(f_{n, t, m \% t}\),就尝试用这个状态更新答案。

需要特别注意对每一个 t 是否存在合法的最终状态。

#include <iostream>
#include <cstring>

using namespace std;

long long f[110][110][110], a[110];

int main() {
    int n;
    long long m, res;
    cin >> n >> m;
    res = m;
    for (int i = 1; i <= n; ++i) {
        cin >> a[i];
    }
    for (int t = 1; t <= n; ++t) {
        memset(f, -1, sizeof(f));
        f[0][0][0] = 0;
        for (int i = 1; i <= n; ++i) {
            f[i][0][0] = 0;
            // i 个 药水
            for (int j = 1; j <= t; ++j) {
                // 选了 j 个
                for (int k = 0; k < t; ++k) {
                    // 余数是 k
                    f[i][j][k] = f[i - 1][j][k];
                    if (~f[i - 1][j - 1][((k - a[i]) % t + t) % t])
                        f[i][j][k] = max(f[i][j][k], f[i - 1][j - 1][((k - a[i]) % t + t) % t] + a[i]);
                }
            }
        }
        if (~f[n][t][m % t]) res = min(res, (m - f[n][t][m % t]) / t);
    }
    cout << res << endl;
    return 0;
}
0
Subscribe to my newsletter

Read articles from Star directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Star
Star

A Chinese OIer and a tosser.