A. Happy New Year 2025

模拟

代码实现
a, b = map(int, input().split())
print((a+b)**2)

B. 9x9 Sum

模拟

代码实现
#include <bits/stdc++.h>
#define rep1(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
int main() {
int x;
cin >> x;
int ans = 0;
rep1(i, 9)rep1(j, 9) {
if (i*j != x) ans += i*j;
}
cout << ans << '\n';
return 0;
}

C. Snake Numbers

讨论小于 x 的数会比较方便,所以不妨设 f(x) 表示小于 x 的蛇数个数
那么答案就是 f(r+1)f(l)

假设 xn 位数
可以先统计不足 n 位数的蛇数个数
对于这种情况,只需考虑枚举每位数的开头的数字 h,后面每一位上可以选的数字为 0h1,所以方案数为 hk1

再来考虑 n 位数的贡献
当开头的数字 h 小于 x 的开头数字时,后面 n1 位都有 0h1 个数可以选择,所以方案数为 hn1

最后再来考虑开头的数字填 x 的开头数字时,再分别考虑后面的每一位,假设当前枚举到第 i 位,如果 x 在这一位上的数字大于等于开头的数字,那么贡献就是 hni(意思就是这一位到最低位上的数字都有 0h1 种选择),并立即跳出循环;否则,在当前位上可以填 0x 在第 i 位上的数 1,后面所有位可以填 0h1

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using ll = long long;
ll pw(ll x, int p) {
ll res = 1;
rep(i, p) res *= x;
return res;
}
ll f(ll r) {
r++;
vector<int> digits;
for (char c : to_string(r)) {
digits.push_back(c-'0');
}
int n = digits.size();
ll res = 0;
for (int k = 1; k < n; ++k) {
for (int h = 1; h <= 9; ++h) res += pw(h, k-1);
}
for (int h = 1; h < digits[0]; ++h) res += pw(h, n-1);
int h = digits[0];
for (int i = 1; i < n; ++i) {
if (digits[i] >= h) {
res += pw(h, n-i);
break;
}
res += pw(h, n-i-1) * digits[i];
}
return res;
}
int main() {
ll l, r;
cin >> l >> r;
ll ans = f(r) - f(l-1);
cout << ans << '\n';
return 0;
}

D. Snaky Walk

需要维护方向的bfs,但由于这里是二维网格所以不需要维护方向

考虑以上的网格图,所有黑格子的横纵坐标之和的积偶性是一样的,为偶数,对于白格子也是,为奇数

行走路径的方向为

--|--|--

|--|--|--

可以通过枚举奇偶性来确定这两种情况

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using P = pair<int, int>;
int main() {
int h, w;
cin >> h >> w;
vector<string> s(h);
rep(i, h) cin >> s[i];
const int INF = 1001001001;
int ans = INF;
rep(par, 2) {
vector dist(h, vector<int>(w, INF));
queue<P> q;
auto push = [&](int i, int j, int d) {
if (i < 0 or j < 0 or i >= h or j >= w) return;
if (s[i][j] == '#') return;
if (dist[i][j] != INF) return;
dist[i][j] = d;
q.emplace(i, j);
};
rep(i, h)rep(j, w) if (s[i][j] == 'S') push(i, j, 0);
while (q.size()) {
auto [i, j] = q.front(); q.pop();
int d = dist[i][j];
if (s[i][j] == 'G') ans = min(ans, d);
if ((i+j)%2 == par) {
push(i-1, j, d+1);
push(i+1, j, d+1);
}
else {
push(i, j-1, d+1);
push(i, j+1, d+1);
}
}
}
if (ans == INF) ans = -1;
cout << ans << '\n';
return 0;
}

E. Digit Sum Divisible 2

一道贴近ARC风格的题

n 不超过 6 位数时,直接暴力即可
对于一般情况,可以考虑构造 2024/2025 这样一边满足数位和是 8 且是8的倍数,另一边满足数位和是 9 且是 9 的倍数的数
考虑只取 n 的高位前三位+1,然后通过不断加 1,把它变成数位和是 8,接下来只需在后面添加若干个 0 即可

代码实现
#include <bits/stdc++.h>
using namespace std;
int keta(int a) {
int res = 0;
for (char c : to_string(a)) {
res += c-'0';
}
return res;
}
bool good(int a) {
return a%keta(a) == 0;
}
int main() {
string s;
cin >> s;
if (s.size() <= 6) {
int n = stoi(s);
for (int a = n; a < n*2; ++a) {
if (good(a) and good(a+1)) {
cout << a << '\n';
return 0;
}
}
puts("-1");
return 0;
}
int n = stoi(s.substr(0, 3))+1;
while (keta(n) != 8) n++;
cout << n << string(s.size()-3, '0') << '\n';
return 0;
}

F. Count Arrays

考虑对点 i 连一条有向边指向点 Ai,这样就得到了一个基环树森林
对于一个基环树而言,环里的点的点权一定是相同的,那么我们可以将整个环缩点。
另外,这个基环树是个内向树
然后对缩点后的树跑树形dp
dp[v][i] 表示在以点 v 为根的子树中满足 xv=i 的方案数
转移方程:

dp[v][i]=cjidp[c][j]

时间复杂度为 O(NM2)
考虑对 jidp[c][j] 这部分进行加速,可以利用前缀和

代码实现
#include <bits/stdc++.h>
#include <atcoder/all>
using namespace atcoder;
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using mint = modint998244353;
int main() {
int n, m;
cin >> n >> m;
vector<int> a(n);
rep(i, n) cin >> a[i], a[i]--;
scc_graph g(n);
rep(i, n) g.add_edge(i, a[i]);
auto groups = g.scc();
int s = groups.size();
vector<int> gid(n);
rep(i, s) for (int v : groups[i]) gid[v] = i;
vector<int> pa(s, s);
rep(i, n) {
int u = gid[i], v = gid[a[i]];
if (u == v) continue;
pa[u] = v;
}
vector dp(s+1, vector<mint>(m, 1));
rep(i, s) {
rep(j, m-1) dp[i][j+1] += dp[i][j];
rep(j, m) dp[pa[i]][j] *= dp[i][j];
}
mint ans = dp[s][m-1];
cout << ans.val() << '\n';
return 0;
}