谁的生活不迷茫,谁的人生不迷惑。每个人都希望未卜先知,都希望掌控自己命运的罗盘,可变换莫测的世界总让我们感到失望,其实,我们的困惑并不在身外,而在我们的内心深处。我们总是希望心灵的疑惑得带一个真诚的回应,即便这个回应只是简单的“是”或“否”。——《答案之书》
引言与比赛无关,只是最近看到一段有意思的文字。
B
签到题,我要是少两发罚时,手速再快一点就好了。
1 2 3 4 5 6 7 8 9 10 11 12 |
s = input() res = '' for c in s: if c in "aeiou": res += c.upper() else: if c == 'z': c = 'a' else: c = chr(ord(c) + 1) res += c print(res) |
A
签到题,手速还是不够快。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
s = input() res = 0 for i in range(1, len(s)): a = int(s[:i]) b = int(s[i:]) # print(a, b) cnt = 0 c = str(a + b) for j in c[::-1]: if j == '0': cnt += 1 else: break res = max(res, cnt) print(res) |
C
第一反应是一个组合数学,根据题意很容易写出答案就是:
$$
\sum_{i=1}^{n-1}\sum_{j=1}^{n-i}C_{n}^{i}\times C_{n-i}^{j}
$$
尝试化简这个式子,发现无从下手。于是乎转换思路,考虑递推。
对于 $f(n)$ 来说,考虑前一项如何转移。假设先选 $n-1$ 个数字,那么对于每一个可能的 $(A,B)$,$n$ 这个数字可能放入 $A$,也可能放入 $B$,也可能都不放入,这样就产生了 $3\times f(n-1)$ 的贡献。对于一个子集单独为 $\{n\}$ 的情况,显然另一个子集需要在 $n-1$ 个数里选,那么就有 $2^{n-1}-1$ 种选法,考虑到有序对,故乘 $2$。所以递推式为 $f(n)=3f(n-1)+2^n-2$,起点为 $f(1)=0$。
那么现在需要求通项公式,这里我就不会了,还是靠我队友算的。反正凑配一下就可以发现通项公式就是 $f(n)=3^{n}-2^{n+1}+1$,那么打一个快速幂就可以了。
一开始我以为这题可能是一道矩阵快速幂的题目,不过没想到直接就把通项公式求出来了。
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 |
#include <bits/stdc++.h> using LL = long long; constexpr LL mod = 1e9 + 7; LL fpow(LL a, LL b, LL mod) { LL res = 1; for (; b; b >>= 1) { if (b & 1) res = res * a % mod; a = a * a % mod; } return res; } void solve() { LL x; std::cin >> x; LL res = fpow(3, x, mod) - fpow(2, x + 1, mod) + 1; res = (res % mod + mod) % mod; std::cout << res << '\n'; } int main() { std::ios::sync_with_stdio(false); std::cin.tie(nullptr); std::cout.tie(nullptr); int T = 1; // std::cin >> T; while (T --) { solve(); } } |
E
看到群里的描述或许这题是被我卡常过的。我的思路是枚举 $j,k$,对于每一个 $a[j]\ XOR\ a[k]$ 用 trie 树查找最优的 $a[i]$。实际上当时超时了,把 std::vector
换成数组,然后用快读才过了。不太清楚正解是啥。
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 |
#include <bits/stdc++.h> template<typename T> void chkmin(T &a, const T &b) { if (a > b) a = b; } template<typename T> void read(T &x) { char ch = getchar(); bool flag = 0; x = 0; for (; ch < '0' || ch > '9'; ch = getchar()) flag |= (ch == '-'); for (; ch >= '0' && ch <= '9'; ch = getchar()) x = (x << 1) + (x << 3) + ch - '0'; if (flag) x = -x; } constexpr int N = 5e3 + 10, M = 5e5 + 10; int a[N], son[M][2], idx; void insert(int x) { int p = 0; for (int i = 21; ~i; -- i) { int u = x >> i & 1; if (!son[p][u]) son[p][u] = ++ idx; p = son[p][u]; } } int query(int x) { int res = 0; int p = 0; for (int i = 21; ~i; -- i) { int u = x >> i & 1; int v = u ^ 1; if (son[p][u]) { p = son[p][u]; } else { p = son[p][v]; res |= 1 << i; } } return res; } void solve() { int n; read(n); for (int i = 1; i <= n; ++ i) read(a[i]); int res = 2e9; for (int i = 1; i <= n - 2; ++ i) { insert(a[i]); int j = i + 1; for (int k = j + 1; k <= n; ++ k) { chkmin(res, query(a[j] ^ a[k])); } } std::cout << res << '\n'; } int main() { solve(); } |
D
一眼看到这题感觉是一道非常 DP 的题目,但是一时间没有很快想到思路。赛时的时候 T 了两发 E 题,然后换队友做这题,但是队友 WA 了,于是换我来写 E,结果没想到就改了常数就过了。然后开始思考这题。
我们可以考虑 $f_i$ 表示有多少点可以到达 $i$ 点,$g_i$ 表示从 $i$ 点出发能到达多少点。那么路径可以分成三类。以 $i$ 为起点,终点和中间点。那么答案应该就是 $ans_i=f_i\times g_i+f_i+g_i+1$。(至于为什么要+1,其实我还没想明白,反正加了就对了)
考虑如何计算 $f_i$、实际上 $f_i,g_i$ 的计算方式是一致的,只不过后者是在反图上求。这里就是做一下 DP,容易看出 $f_v=\sum_{(u,v)\in G}{(f_u+1)}$。DP 的话要保证合法的顺序,所以先做一遍拓扑排序。
实际上我们需要考虑重边的情况。容易想到有几条重边,走法就会多几倍。所以只需要记录一下边出现的次数即可,最后的答案为:
$$
f_v=\sum_{(u,v)\in G}{(f_u+1)}\times cnt(u,v)
$$
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 72 73 74 75 76 |
#include <bits/stdc++.h> using LL = long long; using PII = std::pair<int, int>; constexpr LL mod = 1e9 + 7; constexpr int N = 1e5 + 10; std::vector<int> topesort(int n, std::vector<std::vector<int>> &edge, std::vector<int> &d) { std::vector<int> q(n); int tt = -1, hh = 0; for (int i = 1; i <= n; ++ i) if (!d[i]) q[++ tt] = i; while (hh <= tt) { int t = q[hh ++]; for (auto v : edge[t]) { d[v] --; if (not d[v]) q[++ tt] = v; } } return q; } void solve() { int n, m; std::cin >> n >> m; std::vector<std::vector<int>> edge(n + 1), uedge(n + 1); std::vector<int> d(n + 1, 0), ud(n + 1, 0); std::map<PII, int> cnt; while (m --) { int u, v; std::cin >> u >> v; if (not cnt[{u, v}]) { edge[u].push_back(v); uedge[v].push_back(u); d[v] ++, ud[u] ++; } cnt[{u, v}] ++; cnt[{v, u}] ++; } auto calc = [&](std::vector<std::vector<int>> &edge, std::vector<int> &d) -> std::vector<LL> { auto q = topesort(n, edge, d); std::vector<LL> f(n + 1, 0); for (int i = 0; i < n; ++ i) { int v = q[i]; for (auto u : edge[v]) { int time = cnt[{v, u}]; LL add = (f[v] + 1) * time % mod; f[u] = (f[u] + add) % mod; } } return f; }; auto f = calc(edge, d); auto g = calc(uedge, ud); for (int i = 1; i <= n; ++ i) { LL res = f[i] * g[i] % mod; res = (res + f[i] + 1) % mod; res = (res + g[i]) % mod; std::cout << res << '\n'; } } int main() { std::ios::sync_with_stdio(false); std::cin.tie(nullptr); std::cout.tie(nullptr); solve(); } |
F
其实我没啥思路,%一下队里 AK 的佬,我自己确实还是得训。
发表回复