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]);
}
浙公网安备 33010602011771号