2025春训第十五场

StarStar
8 min read

A. Lucky 7

一道签到题。

#include <iostream>

using namespace std;

bool cont(int n) {
    for (char c : to_string(n)) {
        if (c == '7') return true;
    }
    return false;
}

int main() {
    int n;
    cin >> n;
    bool c1 = cont(n), c2 = n % 7 == 0;
    if (!c1 && !c2) cout << 0 << endl;
    else if (!c1 && c2) cout << 1 << endl;
    else if (c1 && !c2) cout << 2 << endl;
    else cout << 3 << endl;
    return 0;
}

B. We Want You Happy!

两道签到题。

#include <iostream>
#include <algorithm>

using namespace std;

struct Customer
{
    int id, s, d, t;
} a[1010];

int main() {
    int n;
    cin >> n;
    for (int i = 1; i <= n; ++i) {
        cin >> a[i].id >> a[i].s >> a[i].d >> a[i].t;
    }
    sort(a + 1, a + n + 1, [](Customer a, Customer b) {
        return a.s < b.s;
    });
    int curt = 0;
    for (int i = 1; i <= n; ++i) {
        if (curt > a[i].s + a[i].t) continue;
        if (curt < a[i].s) curt = a[i].s;
        curt += a[i].d;
        cout << a[i].id << endl;
    }
    return 0;
}

C. Snailography

生成一个下标矩阵然后对着矩阵输出。

#include <iostream>

using namespace std;

int t[30][30];

int main() {
    int n;
    string s;
    cin >> n >> s;
    int x = 0, y = 1;
    int cur = n * n;
    while (cur > 0) {
        while (x + 1 <= n && t[x + 1][y] == 0) t[++x][y] = cur--;
        while (y + 1 <= n && t[x][y + 1] == 0) t[x][++y] = cur--;
        while (x - 1 > 0 && t[x - 1][y] == 0) t[--x][y] = cur--;
        while (y - 1 > 0 && t[x][y - 1] == 0) t[x][--y] = cur--;
    }
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= n; ++j) {
            if (t[i][j] <= s.length()) cout << s[t[i][j] - 1];
        }
    }
    cout << endl;
    return 0;
}

D. Good Goalie

  • 肯定不行的情况,原点到直线的距离大于 r;

  • 肯定是 0 的情况 \(|x|\) ≤ r;

  • 其余情况,先把 x, y 取一下绝对值,对称变换不会影响答案

    答案是蓝色的角 \(\arctan \frac{x}{y} - \arccos \frac{d}{r}\)

#include <iostream>
#include <cmath>
#include <iomanip>

using namespace std;

int main() {
    double y, x, r;
    while (cin >> y >> x >> r) {
        double d = fabs(x * y) / sqrt(x * x + y * y);
        if (d <= r) {
            if (fabs(r) >= fabs(x)) cout << 0 << endl;
            else {
                x = fabs(x), y = fabs(y);
                double theta = atan(x / y) - acos(d / r);
                cout << fixed << setprecision(6) << theta * 180 / acos(-1) << endl;
            }
        }
        else cout << -1 << endl;
    }
    return 0;
}

E. Most Valuable Pez

分组背包。

#include <iostream>

using namespace std;

int f[12010], s[13];

int main() {
    int n, m;
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= 12; ++j) {
            scanf("%d", &s[j]);
            s[j] += s[j - 1];
        }
        for (int j = m; j; --j) {
            for (int k = 1; k <= 12 && k <= j; ++k) {
                f[j] = max(f[j], f[j - k] + s[k]);
            }
        }
    }
    printf("%d\n", f[m]);
    return 0;
}

G. Not So Close

经典的状压dp,用一个二进制数 mask 表示一行的状态。只考虑当前行合法状态需要满足 mask » 1 & mask == 0,因为不能相邻;相邻两行的状态 i,j 需要满足 i « 1 & j == 0 and i & j == 0 and i » 1 & j == 0。

#include <iostream>

using namespace std;

const int MOD = 1000000007;

long long f[1010][1 << 10];

int main() {
    int r, n;
    cin >> r >> n;
    f[0][0] = 1;
    for (int i = 1; i <= n; ++i) {
        for (int j = 0; j < (1 << r); ++j) {
            if (j >> 1 & j) continue;
            for (int k = 0; k < (1 << r); ++k) {
                if ((k >> 1 & k) || (k & j) || (k >> 1 & j) || (k << 1 & j)) continue;
                f[i][j] = (f[i][j] + f[i - 1][k]) % MOD;
            }
        }
    }
    long long res = 0;
    for (int mask = 0; mask < (1 << r); ++mask) {
        res = (res + f[n][mask]) % MOD;
    }
    cout << res << endl;
    return 0;
}

H. The Duel of Smokin’ Joe

统计逆序对数量,如果是奇数就是 Smokin Joe!,否则就是 Oh No!.

原理是一次交换一定会改变逆序对数量的奇偶性,如果原来是奇数,那一定需要奇数次,先手必胜;反之后手必胜。

#include <iostream>

using namespace std;

const int N = 1000010;
int tr[N], a[N], n;

void add(int u) {
    for (; u <= n; u += u & -u) {
        tr[u]++;
    }
}

int query(int u) {
    int res = 0;
    for (; u; u -= u & -u) {
        res += tr[u];
    }
    return res;
}

int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i) {
        scanf("%d", &a[i]);
    }
    long long cnt = 0;
    for (int i = n; i; --i) {
        cnt += query(a[i]);
        add(a[i]);
    }
    if (cnt & 1) printf("Smokin Joe!\n");
    else printf("Oh No!\n");
    return 0;
}

归并排序是什么,感觉不如树状数组()

J. Grow Measure Cut Repeat

因为每次 cut 都是单调不增的,所以不会发生第一次 cut 过的树第二次不 cut 的情况,这就好办了。维护两个线段树,一个维护忽略所有裁剪的前提下的高度,一个维护只考虑最后一次裁剪的情况下的高度。

说起来简单,但是实际上非常不好写,毕竟用线段树维护等差数列本来就比较复杂。具体的,第一颗树需要两个懒标记一个是首项,一个是公差;第二棵树需要三个懒标记,一个首项,一个公差,还有一个覆盖标记(因为裁剪之后要把所有的数覆盖成新的H),同时需要注意边界,我就因为边界快调死了😭。

#include <iostream>

using namespace std;

const int N = 500000;

int seq[N];

struct SemgentTree {
    long long a[N], tag[N];
    bool cover[N];

    void pushdown(int u, int l, int r) {
        if (cover[u]) {
            int mid = (l + r) >> 1;
            a[u << 1] = a[u], a[u << 1 | 1] = a[u] + tag[u] * (mid - l + 1);
            tag[u << 1] = tag[u], tag[u << 1 | 1] = tag[u];
            tag[u] = 0;
            a[u] = 0;
            cover[u << 1] = cover[u << 1 | 1] = true;
            cover[u] = 0;
        }
        if (a[u] || tag[u]) {
            int mid = (l + r) >> 1;
            a[u << 1] += a[u], a[u << 1 | 1] += a[u] + tag[u] * (mid - l + 1);
            tag[u << 1] += tag[u], tag[u << 1 | 1] += tag[u];
            tag[u] = 0;
            a[u] = 0;
        }
    }

    void reset(int u, int l, int r, long long val) {
        cover[u] = true;
        a[u] = val, tag[u] = 0;
    }

    void modify(int u, int l, int r, int ql, int qr, int a1, int d) {
        if (ql > qr) return; // !!!!!
        if (ql <= l && r <= qr) {
            a[u] += a1 + (l - ql) * d; // 首项
            tag[u] += d; // 公差
        }
        else {
            pushdown(u, l, r);
            int mid = (l + r) >> 1;
            if (ql <= mid) modify(u << 1, l, mid, ql, qr, a1, d);
            if (qr > mid) modify(u << 1 | 1, mid + 1, r, ql, qr, a1, d);
        }
    }

    long long query(int u, int l, int r, int p) {
        if (l == r) return a[u];
        else {
            pushdown(u, l, r);
            int mid = (l + r) >> 1;
            if (p <= mid) return query(u << 1, l, mid, p);
            else return query(u << 1 | 1, mid + 1, r, p);
        }
    }
} smt, limt;

void print(int t) {
    for (int i = 1; i <= t; ++i) {
        printf("%lld ", min(limt.query(1, 1, 100000, i), smt.query(1, 1, 100000, i)));
    }
    printf("\n");
}

int main() {
    int q, n = 100000;
    scanf("%d", &q);
    while (q--) {
        char s[2];
        scanf("%s", s);
        if (s[0] == 'A') {
            int l, k;
            scanf("%d%d", &l, &k);
            smt.modify(1, 1, n, max(1, l - k + 1), l, max(1, k - l + 1), 1);
            smt.modify(1, 1, n, l + 1, min(n, l + k - 1), k - 1, -1);
            limt.modify(1, 1, n, max(1, l - k + 1), l, max(1, k - l + 1), 1);
            limt.modify(1, 1, n, l + 1, min(n, l + k - 1), k - 1, -1);
        }
        else if (s[0] == 'B') {
            int p;
            scanf("%d", &p);
            printf("%lld\n", min(smt.query(1, 1, n, p), limt.query(1, 1, n, p)));
        }
        else {
            int H;
            scanf("%d", &H);
            limt.reset(1, 1, n, H);
        }
    }
    return 0;
}

K. Bad Bunny

点双联通分量缩点,然后树上倍增求路径上割点的个数,如果端点不是割点,就额外加上端点。

说着很简单,调的时候也是很折磨,因为我是现学的点双联通分量缩点,之前只学过强联通和边双联通。点双的缩点更复杂,把双联通分量缩点,同时还需要把割点复制一份单独拎出来连接,第一次写的时候容易挂。

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

using namespace std;

const int N = 200010;

int head[N], head2[N], ver[N * 4], ne[N * 4], tot = 1;
int dfn[N], low[N], id[N], t, bcc_cnt, rt;
vector<int> bcc[N];
int cut[N], cut_id[N];
int f[N][20], g[N][20], dep[N];
stack<int> st;

void add(int head[], 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]) {        
            cnt++;
            tarjan(y, i);
            low[x] = min(low[x], low[y]);
            if (dfn[x] <= low[y]) {
                bcc_cnt++;
                int tp;
                if (x != rt || cnt >= 2) cut[x] = true;
                do {
                    tp = st.top();
                    st.pop();
                    id[tp] = bcc_cnt;
                    bcc[bcc_cnt].push_back(tp);
                } while (tp != y);
                bcc[bcc_cnt].push_back(x);
                id[x] = bcc_cnt;
            }
        }
        else {
            low[x] = min(low[x], dfn[y]);
        }
    }
}

void dfs(int x) {
    g[x][0] = cut_id[x];
    for (int i = 1; i < 20; ++i) {
        f[x][i] = f[f[x][i - 1]][i - 1];
        g[x][i] = g[x][i - 1] + g[f[x][i - 1]][i - 1];
    }
    for (int i = head2[x]; i; i = ne[i]) {
        int y = ver[i];
        if (y == f[x][0]) continue;
        f[y][0] = x;
        dep[y] = dep[x] + 1;
        dfs(y);
    }
}

int main() {
#ifndef ONLINE_JUDGE
    freopen("input", "r", stdin);
#endif // !ONLINE_JUDGE
    int n, m;   
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= m; ++i) {
        int x, y;
        scanf("%d%d", &x, &y);
        add(head, x, y);
        add(head, y, x);
    }
    rt = 1;
    tarjan(1, 0);
    for (int i = 1; i <= n; ++i) {
        if (cut[i]) {
            id[i] = ++bcc_cnt;
            cut_id[bcc_cnt] = true;
        }
    }
    for (int i = 1; i <= bcc_cnt; ++i) {
        for (int y : bcc[i]) {
            if (cut[y]) {
                add(head2, id[y], i);
                add(head2, i, id[y]);
            }
        }
    }

    dep[1] = 1;
    dfs(1);
    int q;
    scanf("%d", &q);
    while (q--) {
        int x, y;
        scanf("%d%d", &x, &y);
        int res = !cut[x] + !cut[y];
        x = id[x], y = id[y];
        if (dep[x] < dep[y]) swap(x, y);
        for (int i = 19; i >= 0; --i) {
            if (dep[f[x][i]] >= dep[y]) {
                res += g[x][i];
                x = f[x][i];
            }
        }
        if (x != y) {
            for (int i = 19; i >= 0; --i) {
                if (f[x][i] != f[y][i]) {
                    res += g[x][i] + g[y][i];
                    x = f[x][i], y = f[y][i];
                }
            }
            res += g[x][0] + g[y][0];
            x = f[x][0];
        }
        if (cut_id[x]) res++;
        printf("%d\n", res);
    }
    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.