AtCoder Beginner Contest 425

https://atcoder.jp/contests/abc425/tasks

A - Sigma Cubes

题意:给定一个 N 求 \(\sum_{i=1}^{N}(-1)^i\times i^3\)

void go() {
    int n = nextInt();
    int res = 0;
    for (int i = 1; i <= n; i++) {
        res += i * i * i * ((i & 1) == 1 ? -1 : 1);
    }
    sl(res);
}

B - Find Permutation 2

题意:给定一个数组,数组元素在1~n之间和-1,如果不是 -1 构造的排列当前这个位置就需要和数组元素相等,为 -1 就可以任意。是否存在这样的排列

题解:如果有一个元素出现的次数大于等于2次,一定不存在这样的排列。对于其它的元素可以任意放在-1的位置上。

void go() {
    int n = nextInt();
    int[] a = new int[n + 1];
    int[] cnt = new int[n + 1];
    boolean flg = true;
    for (int i = 1; i <= n; i++) {
        a[i] = nextInt();
        if (a[i] != -1) {
            cnt[a[i]]++;
            if (cnt[a[i]] >= 2) {
                flg = false;
            }
        }
    }
    if (!flg) {
        sl("No");
        return;
    }
    int[] res = new int[n + 1];
    for (int i = 1; i <= n; i++) {
        if (a[i] != -1) {
            res[i] = a[i];
        } else {
            for (int j = 1; j <= n; j++) {
                if (cnt[j] == 0) {
                    res[i] = j;
                    cnt[j]++;
                    break;
                }
            }
        }
    }
    sl("Yes");
    for (int i = 1; i <= n; i++) {
        so(res[i] + " ");
    }
    sl();
}

C - Rotate and Sum Query

题意:给定一个数组,有两种操作:一种是整个数组向左循环移位c次,一种是求[l, r]的区间和。

题解:向左循环移位对区间[l, r]的影响为:左右端点右移对应位置。注意和数组长度取模等于0,和 l > r 的情况。

void go() {
    int n = nextInt(), q = nextInt();
    int[] a = new int[n + 1];
    long[] pre = new long[n + 1];
    for (int i = 1; i <= n; i++) {
        a[i] = nextInt();
        pre[i] = pre[i - 1] + a[i];
    }
    long cnt = 0;
    while (q-- > 0) {
        int op = nextInt();
        if (op == 1) {
            int c = nextInt();
            cnt += c;
        } else {
            int l = nextInt(), r = nextInt();
            l = (int) ((l + cnt) % n);
            r = (int) ((r + cnt) % n);
            l = l == 0 ? n : l;
            r = r == 0 ? n : r;
            if (l <= r) {
                long res = pre[r] - pre[l - 1];
                sl(res);
            } else {
                long res = pre[n] - pre[l - 1] + pre[r];
                sl(res);
            }
        }
    }
}

D - Ulam-Warburton Automaton

题意:给定一个网格,有黑色格子和白色格子,将一个白色染成黑色的条件为:相邻节点有且只有一个黑色格子。

题解:注意是每次白色格子是一起染色。分轮次的BFS,每次队列中的黑色格子找到满足条件的白色格子,直接染成黑色,再加入到队列中。

int[] dx = {-1, 1, 0, 0};
int[] dy = {0, 0, -1, 1};
void go() {
    int n = nextInt(), m = nextInt();
    StringBuilder[] g = new StringBuilder[n];
    for (int i = 0; i < n; i++) {
        g[i] = new StringBuilder(next());
    }
    Queue<int[]> q = new LinkedList<>();
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            if (g[i].charAt(j) == '#') {
                q.add(new int[]{i, j});
            }
        }
    }

    while (!q.isEmpty()) {
        List<int[]> tmp = new ArrayList<>();  // 记录当前这一轮可以染色的白色格子坐标
        while (!q.isEmpty()) {
            int[] t = q.peek(); q.poll();
            int x = t[0], y = t[1];
            for (int i = 0; i < 4; i++) {
                int xx = dx[i] + x, yy = dy[i] + y;
                if (xx < 0 || xx >= n || yy < 0 || yy >= m) continue;
                if (g[xx].charAt(yy) == '#') continue;
                int cnt = 0;
                for (int j = 0; j < 4; j++) {
                    int xxx = dx[j] + xx, yyy = dy[j] + yy;
                    if (xxx < 0 || xxx >= n || yyy < 0 || yyy >= m) continue;
                    if (g[xxx].charAt(yyy) == '#') cnt++;
                    if (cnt >= 2) break;
                }
                if (cnt != 1) continue;
                tmp.add(new int[]{xx, yy});
            }
        }
        for (int[] e : tmp) {  // 一起加入到队列中
            int x = e[0], y = e[1];
            g[x].setCharAt(y, '#');
            q.add(e);
        }
    }
    int cnt = 0;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            if (g[i].charAt(j) == '#') cnt++;
        }
    }
    sl(cnt);
}

E - Count Sequences 2

题意:一个数组,需要求构造排列的方案数。构造的排列中需要包含 \(c_i\)\(i\) 。方案数对 m 取模。这个 m 是输入的。

题解:这是一个多重集排列。\(C(sum, c_1) \times C(sum-c_1, c_2) \times C(sum-c_1-c_2, c_3)......\) 因式分解为 \(\frac{(\sum_{i=1}^{n}c_i)!}{\prod_{i=1}^{n}c_i!}\)

如果m是一个质数,可以使用除式进行逆元。但是m可以不为质数。有两种做法,不是质数的逆元还没学会。

题目中数组的和不超过 5000,可以用递推求出5000之内的所有组合数。就变成加法和乘法了,不需要关心m是否为质数。

static int N = 5010, MOD;
static long[][] C = new long[N][N];
void init() {
    C[0][0] = 1;
    for (int i = 1; i < N; i++) {
        C[i][0] = 1;
        for (int j = 1; j < i; j++) {
            C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % MOD;
        }
        C[i][i] = 1;
    }
}
void go() {
    int n = nextInt();
    int sum = 0;
    int[] a = new int[n];
    for (int i = 0; i < n; i++) {
        a[i] = nextInt();
        sum += a[i];
    }
    long res = 1;
    for (int i = 0; i < n; i++) {
        res = (res * C[sum][a[i]]) % MOD;
        sum -= a[i];
    }
    sl(res);
}

F - Inserting Process

题意:给定一个长度为N的字符串\((N\leq22)\) 。可以构造出 N + 1 个序列,第一个序列为空,然后依次添加一个字符,要求只能在前一个字符串的基础上添加一个字符,使得第 N+1 个字符串为给定的字符串。求有多少种构造的序列方法。

题解:其实就是从空变成目标字符串的方案数。设当前构造成的字符串状态为 i,二进制下为1的位置对应目标字符串位置的字符。那么可以从前面状态推出当前状态:f[i] += f[j]. j 为前一个状态。j 这个状态如何得到呢?可以枚举当前状态 i,如果当前位置为 1,证明存在这个字符,那么从没有这个字符的状态递推过来。没有这个字符的状态就是 j。
但是这样会存在重复的计算,比如aab,当前状态为 \((111)_2\) 那么枚举到第一位 1 的时候,从状态 \((011)_2\) 递推,第二位 1 的时候从状态 \((101)_2\) 递推,这两个状态对应的字符串都是 ab。那么就重复计算了两次,所以对于这种连续相同字符段,可以只取第一个或最后一个。所以可以记录上一轮枚举的字符,每次不能相等即可。

static int MOD = 998244353;
void go() {
    int n = nextInt();
    String s = next();
    int[] f = new int[1 << n];
    f[0] = 1;
    for (int st = 1; st < 1 << n; st++) {
        char last = ' ';
        for (int j = 0; j < n; j++) {
            if ((st >> j & 1) == 1 && last != s.charAt(j)) {
                f[st] = (f[st] + f[st ^ (1 << j)]) % MOD;
                last = s.charAt(j);
            }
        }
    }
    sl(f[(1 << n) - 1]);
}
posted @ 2025-10-24 18:34  yairr  阅读(7)  评论(0)    收藏  举报