跳到主要内容

ABC436

Problem A​

题目描述​

给你一个整数 NN 和一个由小写英文字母组成的字符串 SS,且字符串长度小于 NN。 请你不断地在字符串 SS 的开头添加小写字母 o,直到它的长度达到 NN 为止,然后输出最终得到的字符串。

数据范围​

  • 2≤N≤1002 \le N \le 100
  • NN 是一个整数。
  • SS 是一个由小写英文字母组成的字符串,长度在 11 到 N−1N - 1 之间(包含边界)。

题解​

只需要先输出 N−∣S∣N - |S| 个 o 后,再输出字符串 SS 即可。

Problem B​

题目描述​

给你一个奇数 NN,且它至少是 33。 有一个 NN 行 NN 列的网格,所有格子一开始都是空的。接下来,你要按照下面的步骤,在每个格子里写入整数。设 (i,j)(i, j) 表示从上往下第 (i+1)(i+1) 行、从左往右第 (j+1)(j+1) 列的格子 (0≤i<N,0≤j<N0 \le i < N, 0 \le j < N)。

  1. 在格子 (0,N−12)(0, \frac{N-1}{2}) 写入数字 11。
  2. 重复下面的操作 N2−1N^2 - 1 次:
    • 设 (r,c)(r, c) 是上一次写数字的格子,写入的数字是 kk。如果格子 ((r−1) mod N,(c+1) mod N)((r - 1) \bmod N, (c + 1) \bmod N) 为空,就在该格子写入 k+1k + 1;否则,在格子 ((r+1) mod N,c)((r + 1) \bmod N, c) 写入 k+1k + 1。这里,x mod Nx \bmod N 是 xx 除以 NN 的余数。

求出按照这个过程,每个格子里最终写入的整数。可以证明,每个格子都会且只会被写入一次。

数据范围​

  • 3≤N≤993 \le N \le 99
  • NN 是一个奇数。

题解​

按照题意直接模拟即可。

Problem C​

题目描述​

有一个 NN 行 NN 列的网格。令 (i,j)(i, j) 表示从上数第 ii 行、从左数第 jj 列的单元格。初始时,网格上没有放置任何物品。

你将执行 MM 次操作。第 ii 次操作(1≤i≤M1 \le i \le M)如下:

  • 当且仅当一个 2×22 \times 2 的方块的放置位置与网格上已有的任何方块都不重叠时,将该方块放置在以单元格 (Ri,Ci)(R_i, C_i) 为左上角的区域。更准确地说,对于单元格集合 S={(Ri,Ci),(Ri+1,Ci),(Ri,Ci+1),(Ri+1,Ci+1)}S = \{(R_i, C_i), (R_i + 1, C_i), (R_i, C_i + 1), (R_i + 1, C_i + 1)\},如果网格上已有的方块占据了 SS 中的任何一个单元格,则不执行任何操作;否则,放置一个占据 SS 中全部四个单元格的方块。

执行完所有操作后,请找出网格上一共放置了多少个方块。

数据范围​

  • 2≤N≤1092 \le N \le 10^9
  • 1≤M≤2×1051 \le M \le 2 \times 10^5
  • 1≤Ri,Ci≤N−11 \le R_i, C_i \le N - 1
  • 所有输入值均为整数。

题解​

可以发现,如果我们 (r1,c1)(r_1, c_1) 和 (r2,c2)(r_2, c_2) 两个 block 有重合,当且仅当 ∣r1−r2∣≤1|r_1 - r_2| \le 1 且 ∣c1−c2∣≤1|c_1 - c_2| \le 1。 所以我们只需要用 set 维护现在的 block 的左上角的集合,每次先检查 [r−1,r+1]×[c−1,c+1][r-1, r+1] \times [c-1, c+1] 有没有已经放好的格子,如果没有的话当前的 block 就可以加入 set。

参考代码​

#include <bits/stdc++.h>
using namespace std;

set<pair<int, int>> s;
bool check(int r, int c) {
for (int dx = -1; dx <= 1; dx++) {
for (int dy = -1; dy <= 1; dy++) {
if (s.find({r + dx, c + dy}) != s.end()) return false;;
}
}
return true;
}

int main() {
int n, m;
cin >> n >> m;

int ans = 0;
for (int i = 1; i <= m; i++) {
int r, c; cin >> r >> c;
if (check(r, c)) {
ans++;
s.insert({r, c});
}
}
cout << ans << endl;
}

Problem D​

题目描述​

有一个迷宫,由一个 HH 行 WW 列的网格组成。用 (i,j)(i, j) 表示位于从上往下第 ii 行、从左往右第 jj 列的格子。格子 (i,j)(i, j) 的类型由一个字符 Si,jS_{i,j} 表示,每个字符代表的含义如下:

  • .:空白格子
  • #:障碍格子
  • 小写英文字母 (a - z):传送格子

在迷宫中,你可以任意次数、任意顺序地执行以下两种操作:

  • 走路:从当前格子移动到上下左右相邻的一个格子,但不能走到障碍格子或迷宫外。
  • 传送:当你站在一个传送格子时,可以瞬间移动到任何一个带有相同字母的传送格子。

请判断是否能从格子 (1,1)(1, 1) 移动到格子 (H,W)(H, W),如果能,求出完成这一步所需的最少操作次数。

数据范围​

  • 1≤H,W≤10001 \le H, W \le 1000
  • H×W≥2H \times W \ge 2
  • HH 和 WW 都是整数。
  • Si,jS_{i,j} 是 .、# 或小写英文字母。
  • S1,1≠#S_{1,1} \neq \#
  • SH,W≠#S_{H,W} \neq \#

题解​

按照题意直接 BFS 即可:

  • 在每一个格子,可以选择上下左右走到相邻的不是 # 的格子。
  • 如果当前格子是 warp,则可以从这个格子到达所有相同字母的格子。

直接跑上面的 BFS 会导致 TLE,核心原因是走 warp 的路径太多了。但我们发现同一个字母对应的 warp 其实只需要被遍历一遍就好了,所以我们也给每个 warp 设置一个 vis 数组。如果这个 warp 已经通过其他位置被处理过了,后续就不再重复扫描该字母对应的所有位置。

参考代码​

#include <bits/stdc++.h>
using namespace std;

const int N = 1000 + 5;
char mp[N][N];
int h, w;

bool vis[N][N];
int dis[N][N];

int dx[4] = {-1, 1, 0, 0};
int dy[4] = {0, 0, -1, 1};
vector<pair<int, int>> qq[30];

struct node {
int x, y;
bool operator< (const node &b) const {
return dis[x][y] > dis[b.x][b.y];
}
};

bool v[100];

void dijkstra() {
queue<pair<int, int>> q;
q.push({1, 1});
dis[1][1] = 1;
while (q.size()) {
auto [x, y] = q.front(); q.pop();

for (int i = 0; i < 4; i++) {
int nx = x + dx[i], ny = y + dy[i];
if (dis[nx][ny]) continue;
if (mp[nx][ny] == '#') continue;

dis[nx][ny] = dis[x][y] + 1;
q.push({nx, ny});
}
if (mp[x][y] != '.') {
if (v[mp[x][y] - 'a' + 1]) continue;
v[mp[x][y] - 'a' + 1] = true;
for (auto [nx, ny] : qq[mp[x][y] - 'a' + 1]) {
if (dis[nx][ny]) continue;
dis[nx][ny] = dis[x][y] + 1;
q.push({nx, ny});
}
}
}
cout << (dis[h][w] == 0x3f3f3f3f ? -1 : dis[h][w] - 1) << endl;
}

int main() {
cin >> h >> w;
for (int i = 0; i <= w + 1; i++) mp[0][i] = mp[h + 1][i] = '#';
for (int i = 0; i <= h + 1; i++) mp[i][0] = mp[i][w + 1] = '#';
for (int i = 1; i <= h; i++) {
for (int j = 1; j <= w; j++) {
cin >> mp[i][j];
if (mp[i][j] >= 'a' && mp[i][j] <= 'z') qq[mp[i][j] - 'a' + 1].push_back({i, j});
}
}

dijkstra();
}

Problem E​

题目描述​

给你一个整数序列 P=(P1,P2,…,PN)P = (P_1, P_2, \dots, P_N),它是 (1,2,…,N)(1, 2, \dots, N) 的一个排列。这里保证 PP 不等于 (1,2,…,N)(1, 2, \dots, N)。 你想通过执行以下操作零次或多次,让 PP 变成序列 (1,2,…,N)(1, 2, \dots, N):

  • 选择一对满足 1≤i<j≤N1 \le i < j \le N 的整数 (i,j)(i, j),交换 PiP_i 和 PjP_j 的值。

设 KK 是让 PP 变成序列 (1,2,…,N)(1, 2, \dots, N) 所需的最少操作次数。 请你求出,有多少种操作可以作为第一步操作,使得通过一系列操作,PP 能在 KK 步内变成 (1,2,…,N)(1, 2, \dots, N)。 两个操作只有在选的整数对 (i,j)(i, j) 不同的情况下才算不同。

数据范围​

  • 2≤N≤3×1052 \le N \le 3 \times 10^5
  • 1≤Pi≤N1 \le P_i \le N (1≤i≤N1 \le i \le N)
  • Pi≠PjP_i \neq P_j (1≤i<j≤N1 \le i < j \le N)
  • 存在 1≤i≤N1 \le i \le N 使得 i≠Pii \neq P_i。
  • 所有输入值均为整数。

题解​

首先根据经典的排列相关理论,将 ii 和 PiP_i 连边构成一张图,可以证明最小的交换次数就是 n−环的数量n - \text{环的数量}。 所以最优的交换操作一定要保证每一步都是让环的数量增加一,而这只有交换同一个环上的两个元素才能实现。 所以设这个图有 mm 个环,大小为 sis_i,答案就是 ∑i=1m(si2)\sum_{i=1}^m \binom{s_i}{2}。

参考代码​

#include <bits/stdc++.h>
using namespace std;

const int N = 3e5 + 10;

int n, p[N];
bool v[N];

int main() {
cin >> n;
for (int i = 1; i <= n; i++) cin >> p[i];
long long ans = 0;
for (int i = 1; i <= n; i++) {
if (v[i]) continue;
v[i] = true;
int cnt = 1;
int t = i;
while (p[t] != i) t = p[t], v[t] = true, cnt++;
ans += 1ll * cnt * (cnt - 1) / 2;
}
cout << ans << endl;
}

F - Starry Landscape Photo​

题目描述​

在 AtCoder 星球的夜空中,有 NN 颗星星,这些 NN 颗星星从东到西排成一条直线。第 ii 颗星从东边数起是这群星中第 BiB_i 明亮的。 高桥决定用下面的步骤拍摄夜空:

  1. 选一对整数 (l,r)(l, r),满足 1≤l≤r≤N1 \le l \le r \le N,并调整相机,使得从东边数第 l,(l+1),…,rl, (l+1), \dots, r 颗星都能入镜,且没有其他星星进入画面。
  2. 选一个整数 bb,满足 1≤b≤N1 \le b \le N,打开快门,捕捉所有在这 NN 颗星中,亮度排名从第 11 到第 bb 的星星(且这些星星都在画面内),且不拍摄其他星星。

数据范围​

  • 1≤N≤5×1051 \le N \le 5 \times 10^5
  • 1≤Bi≤N1 \le B_i \le N (1≤i≤N1 \le i \le N)
  • Bi≠BjB_i \neq B_j (1≤i<j≤N1 \le i < j \le N)
  • 所有输入的值均为整数.

但不能拍出一张没有星星的照片哦。 请你计算,有多少种不同的星星组合能被这样拍摄到?

题解​

考虑我们先从小到大枚举最终的集合中最大的数字是 MM,那么此时可以假设操作 (2) 中的 b=Mb = M。 那么此时相当于首先只剩下了所有 ≤M\le M 的数字,其次我们所有包含 MM 的子区间都对应一种唯一的集合。 设数字 ii 左边比它小的数字个数为 lil_i,则子区间数量有 (li+1)(i−li)(l_i + 1)(i - l_i) 个。 由于最大数字不同时,显然对应不同的集合,所以总答案就是 ∑i=1n(li+1)(i−li)\sum_{i=1}^n (l_i + 1)(i - l_i)。 而 lil_i 可以通过扫一遍+用树状数组/权值线段树/平衡树维护求得。

Problem G​

题目描述​

给你一个长度为 NN 的正整数序列 A=(A1,A2,…,AN)A = (A_1, A_2, \dots, A_N),还有一正整数 MM。 请你求出满足以下条件的长度为 NN 的非负整数序列 x=(x1,x2,…,xN)x = (x_1, x_2, \dots, x_N) 的数量: ∑i=1NAixi≤M\sum_{i=1}^N A_i x_i \le M 答案可能非常大,请对 998244353998244353 取模后输出。

题解​

首先我们可以通过补充一个 Ai=1A_i = 1 的物品,将题目的限制变为 ∑i=1NAixi=M\sum_{i=1}^N A_i x_i = M。

而这是一个标准的卷积问题。第 ii 个物品的生成函数是 1+xAi+x2Ai+⋯=11−xAi1 + x^{A_i} + x^{2A_i} + \dots = \frac{1}{1 - x^{A_i}}。

所以这个题目的答案就是:

[xM]1∏i=1N(1−xAi)[x^M] \frac{1}{\prod_{i=1}^N (1 - x^{A_i})}

记分母的式子为 Q(x)Q(x),其实就是要求 [xM]1Q(x)[x^M] \frac{1}{Q(x)}。但是 MM 太大了,直接多项式求逆做不了。

注意这里 QQ 的次数 ≤104\le 10^4,而我们又知道多项式求逆本质上就是一个常系数线性递推,所以当然可以构造出对应的线性递推式子后用常系数线性递推做。

但其实还有另一种更简便的方法:Bostan-Mori 算法。这个算法专门用来解决 [xM]P(x)Q(x)[x^M] \frac{P(x)}{Q(x)} 的计算(其中 P,QP, Q 是有理多项式):

首先我们可以将原式变为 [xM]P(x)Q(−x)Q(x)Q(−x)[x^M] \frac{P(x)Q(-x)}{Q(x)Q(-x)},可以发现 Q(x)Q(−x)Q(x)Q(-x) 是偶函数,所以只有偶数项。

所以我们可以记 Q(x)Q(−x)=T(x2)Q(x)Q(-x) = T(x^2)。此外我们将 P(x)Q(−x)P(x)Q(-x) 按奇偶分类可以写成 P(x)Q(−x)=xO(x2)+E(x2)P(x)Q(-x) = xO(x^2) + E(x^2)。

问题就变为 [xM]xO(x2)T(x2)+[xM]E(x2)T(x2)[x^M] \frac{xO(x^2)}{T(x^2)} + [x^M] \frac{E(x^2)}{T(x^2)}。这样对 MM 进行奇偶分类讨论后,就可以递归到两个中的其中一个去递归计算。此时 MM 就会减半。而 O,E,TO, E, T 的多项式次数与 P,QP, Q 还是相同的。