E - Replace转换为图论问题,S[i]
指向 T[i]
建边。相当于 26 个点,每一个点的出度最多为 1,否则无解。
答案基本上等于边的数量 - 自环的数量。只是存在环的联通块需要特殊考虑。
这个环上的每一个点入度出度都等于 1,出现死循环,需要拆环。而拆环需要找到一个入度为 0 的点,也就是判断是否存在一个中间点。如果存在,那么答案+1,否则无解。
这个环上,存在某个点 X
的入度大于 1,又已知每个点的出度最大为 1,那么假设有环上某点 Y
,环外某点 Z
指向 X
。 于是可以将环拆开,将 Y -> X
转变为 Y -> Z
。先做一次 Y-> Z
,那么环就拆成了链条。这不产生额外开销。
图解:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 #include <bits/stdc++.h> bool vis[26 ][26 ];int row[26 ], col[26 ], nxt[26 ];int res = 0 ;int main () { int N; std::cin >> N; std::string S, T; std::cin >> S >> T; for (int i = 0 ; i < N; i++) { vis[S[i] - 'a' ][T[i] - 'a' ] = true ; } for (int i = 0 ; i < 26 ; i++) { for (int j = 0 ; j < 26 ; j++) { row[i] += vis[i][j]; col[j] += vis[i][j]; if (vis[i][j]) nxt[i] = j; } if (row[i] >= 2 ) { std::cout << -1 << std::endl; return 0 ; } } bool flag = false ; for (int i = 0 ; i < 26 ; i++) { if (col[i] == 0 ) flag = true ; if (row[i] == 0 || col[i] != 0 ) continue ; int u = i; while (row[u] != 0 ) { res++; row[u] = 0 ; u = nxt[u]; if (u == nxt[u]) break ; } } int cnt = 0 ; for (int i = 0 ; i < 26 ; i++) { if (row[i] == 0 || nxt[i] == i) continue ; int u = i; while (row[u] != 0 ) { cnt++; row[u] = 0 ; u = nxt[u]; } cnt++; } if (cnt != 0 && flag == false ) { std::cout << -1 << std::endl; return 0 ; } res += cnt; std::cout << res << std::endl; return 0 ; }
赛中想到了转化为图论,想到了拆环。花了很长时间想到了特殊情况 2。
赛后发现,第一次遍历连通块时没有考虑到自环。
F - Range Power Sum 计数题,把表达式转化一下就好了。
图解:
在推式子的时候,二项式展开忘记了组合数,S 的边界值考虑的不清楚。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 #include <bits/stdc++.h> using i64 = long long ;const i64 mod = 998244353 ;int main () { int N, K; std::cin >> N >> K; std::vector<i64> A (N + 1 ) ; for (int i = 1 ; i <= N; i++) std::cin >> A[i]; std::vector<std::vector<i64>> S (N + 1 , std::vector <i64>(K + 1 )); for (int i = 0 ; i <= N; i++) { S[i][0 ] = 1 ; S[i][1 ] = i ? (S[i - 1 ][1 ] + A[i]) % mod : 0 ; for (int k = 2 ; k <= K; k++) S[i][k] = S[i][k - 1 ] * S[i][1 ] % mod; } std::vector<std::vector<i64>> F (N + 1 , std::vector <i64>(K + 1 )); for (int k = 0 ; k <= K; k++) { F[0 ][k] = S[0 ][k]; for (int i = 1 ; i <= N; i++) { F[i][k] = (F[i - 1 ][k] + S[i][k]) % mod; } } std::vector<std::vector<i64>> C (K + 1 , std::vector <i64>(K + 1 )); for (int i = 1 ; i <= K; i++) { C[i][0 ] = C[i][i] = 1 ; for (int j = 1 ; j < i; j++) { C[i][j] = (C[i - 1 ][j - 1 ] + C[i - 1 ][j]) % mod; } } i64 res = 0 ; for (int i = 1 ; i <= N; i++) { int flag = 1 ; for (int t = K; t >= 0 ; t--) { i64 val = S[i][t] * flag * F[i - 1 ][K - t] % mod * C[K][t] % mod; val = (val + mod) % mod; res = (res + val) % mod; flag *= -1 ; } } std::cout << res << std::endl; return 0 ; }
之后去看了下题解,思路是 dp,转换为隔板分小球的模型,理解难度不是很大,想法很巧妙。时间复杂度没我的解法优秀。
比赛链接:https://atcoder.jp/contests/abc399 题解:https://atcoder.jp/contests/abc399/editorial