$A$. 小红的$red$ 直接枚举就好
神奇的代码
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 #include <bits/stdc++.h> #define endl '\n' #define int long long const int mod = 998244353 ;void solve () { std::string s; std::cin >> s; for (char c : s) { if (c != 'r' && c != 'e' && c != 'd' ) { std::cout << "No" << endl; return ; } } std::cout << "Yes" << endl; } signed main () { std::ios::sync_with_stdio (false ); std::cin.tie (nullptr ); std::cout.tie (nullptr ); int _t = 1 ; while (_t --) solve (); return 0 ; }
$B$.小红的矩阵构造 构造
我们就让前2个不同就好了,可以发现只有1行或者1列的情况可以,其他就不行了,也要注意$n == 1$ $and$ $m == 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> #define endl '\n' #define int long long const int mod = 998244353 ;void solve () { int n = 0 , m = 0 ; std::cin >> n >> m; if ((n != 1 && m != 1 ) || (n * m == 1 )) { std::cout << -1 << endl; return ; } if (n == 1 ) { std::cout << "10" ; for (int i = 1 ; i <= m - 2 ; i++) std::cout << "0" ; } else { std::cout << "1" << endl; std::cout << "0" << endl; for (int i = 1 ; i <= n - 2 ; i++) std::cout << "0" << endl; } } signed main () { std::ios::sync_with_stdio (false ); std::cin.tie (nullptr ); std::cout.tie (nullptr ); int _t = 1 ; while (_t --) solve (); return 0 ; }
$C$.小红的数据结构 模拟
直接分别用一个$stack$和一个$queue$去模拟整个过程,并记录弹出的数,一共就这四种情况
都能和$a$数组匹配上就是$both$
都匹配不上就是$-1$
$stack$能匹配上就是$stack$
$queue$能匹配上就是$queue$
神奇的代码
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 #include <bits/stdc++.h> #define endl '\n' #define int long long const int mod = 998244353 ;void solve () { int n = 0 , q = 0 ; std::cin >> n >> q; std::stack<int > stk; std::queue<int > que; std::vector<int > a (n, 0 ) , stk_vec, que_vec ; for (int i = 0 ; i < n; i++) std::cin >> a[i]; for (int i = 1 ; i <= q; i++) { int op; std::cin >> op; if (op == 1 ) { int v; std::cin >> v; stk.push (v); que.push (v); } else { int top_stk = stk.top (); int que_stk = que.front (); stk.pop (); que.pop (); stk_vec.push_back (top_stk); que_vec.push_back (que_stk); } } bool stk_ok = true , que_ok = true ; for (int i = 0 ; i < n; i++) if (stk_vec[i] != a[i]) {stk_ok = false ; break ;} for (int i = 0 ; i < n; i++) if (que_vec[i] != a[i]) {que_ok = false ; break ;} if (stk_ok == false && que_ok == false ) {std::cout << -1 << endl; return ;} if (stk_ok == true && que_ok == true ) {std::cout << "both" << endl; return ;} if (stk_ok == true ) {std::cout << "stack" << endl; return ;} if (que_ok == true ) {std::cout << "queue" << endl; return ;} } signed main () { std::ios::sync_with_stdio (false ); std::cin.tie (nullptr ); std::cout.tie (nullptr ); int _t = 1 ; while (_t --) solve (); return 0 ; }
$D.$ 小红的部分字符串 并查集 快速幂
神奇的代码
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 #include <bits/stdc++.h> #define endl '\n' #define int long long const int mod = 998244353 ;int qpower (int y, int x) { int r = 1 ; while (x) { if (x & 1 ) r = r * y % mod; y = y * y % mod; x >>= 1 ; } return r; } int faz[200010 ];int find (int x) { if (faz[x] == x) return x; return faz[x] = find (faz[x]); } void Merge (int x, int y) { int prex = find (x), prey = find (y); faz[prey] = prex; } void solve () { int n = 0 , k = 0 ; std::cin >> n >> k; for (int i = 1 ; i <= n; i++) faz[i] = i; for (int i = 1 ; i <= k; i++) { int su, sv; std::cin >> su >> sv; Merge (su, sv); } int cnt = 0 ; for (int i = 1 ; i <= n; i++) { if (faz[i] == i) cnt++; } std::cout << qpower (26 , cnt) % mod; } signed main () { std::ios::sync_with_stdio (false ); std::cin.tie (nullptr ); std::cout.tie (nullptr ); int _t = 1 ; while (_t --) solve (); return 0 ; }
$E$. 小红的树权值 树的最小点覆盖 树形$dp$
剩余的所有联通块大小都为 $1$ $\rightarrow$删完以后,图里不能再有任何边。因为只要还有一条边连着两个点,那这两个点就在同一个连通块里,这个连通块大小至少是 $2$,不符合要求。
所以“所有连通块大小都为 $1$等价于:
剩下的每个点都是孤立点
任意两个剩余点之间都没有边
这个本质上就是树上的最小点覆盖 :选最少的点,使得每条边都被覆盖
那么就变成了一个树形$dp$的板子题
$dp[u][0]$:在 $u$ 的子树中,保留 $u$ 时,使这棵子树满足要求的最小删除数
$dp[u][1]$:在 $u$ 的子树中,删除 $u$ 时,使这棵子树满足要求的最小删除数
最后:$ans[u] = \min(dp[u][0], dp[u][1])$就是 $u$ 这棵子树的权值。
设 $u$ 的儿子是 $v$。
如果 $u$ 保留:dp[u][0] 因为 $u$ 没删,那么边 $u-v$ 不能留下。
所以对于每个儿子 $v$,**必须删掉 $v$**。
因此:$dp[u][0] = \sum dp[v][1]$
如果 $u$ 删除:dp[u][1] 那边 $(u-v)$ 自然消失了。
这时候每个儿子 $v$ 就自由了:
只要它自己的子树内部合法就行。
所以每个儿子取最优:$dp[u][1] = 1 + \sum \min(dp[v][0], dp[v][1])$
前面的 1 是因为删掉了 (u) 本人。
叶子节点会怎样? 如果 $u$ 是叶子:
保留它:不用删任何点,$dp[u][0]=0$
删除它:删 1 个点,$dp[u][1]=1$
所以:$ans[u] = \min(0,1)=0$
神奇的代码
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 #include <bits/stdc++.h> #define endl '\n' #define int long long const int mod = 998244353 ;std::vector<int > g[100010 ]; void solve () { int n = 0 ; std::cin >> n; for (int i = 1 ; i <= n; i++) g[i].clear (); std::vector<std::array<int , 2>> dp (n + 1 , {0 , 0 }); for (int i = 1 ; i < n; i++) { int u, v; std::cin >> u >> v; g[u].push_back (v); g[v].push_back (u); } auto dfs = [&](auto && dfs, int s, int fa) -> void { dp[s][0 ] = 0 ; dp[s][1 ] = 1 ; for (int v : g[s]) { if (v == fa) continue ; dfs (dfs, v, s); dp[s][0 ] += dp[v][1 ]; dp[s][1 ] += std::min (dp[v][0 ], dp[v][1 ]); } }; dfs (dfs, 1 , 0 ); for (int i = 1 ; i <= n; i++) { std::cout << std::min (dp[i][0 ], dp[i][1 ]) << endl; } } signed main () { std::ios::sync_with_stdio (false ); std::cin.tie (nullptr ); std::cout.tie (nullptr ); int _t = 1 ; std::cin >> _t ; while (_t --) solve (); return 0 ; }
$F$.小红的部分不同字符串 把位置看成点,把限制 s_i != s_{a_i} 看成一条边,那么题目就变成了:
用 26 种颜色给图染色
要求每条边两端颜色不同
求合法染色方案数
这张图的特殊之处在于:每个点恰好连向一个点 a_i,所以它是一个 函数图 。 函数图的每个连通块一定是:
一个环 + 若干棵挂在环上的树
所以整题只需要拆成两部分考虑:
环外的树很好算。
设某个点 u 不在环上,它只需要满足:$s_u != s_{a_u}$
如果 a_u 的颜色已经确定,那么 u 就有 25 种可选颜色。
所以:每个非环点都会给答案贡献一个 25
如果总共有 cnt 个非环点,那么它们总贡献就是: $25^{cnt}$
真正麻烦的是环。
设一个环长为 c,有 q 种颜色时,环的合法染色数是经典公式:$(q-1)^c + (-1)^c (q-1)$
这题里 q = 26,所以长度为 c 的环贡献为:$25^c + (-1)^c \cdot 25$
即:
c 为奇数:25^c - 25
c 为偶数:25^c + 25
所以整题答案就是:
先乘上所有非环点的贡献 25^{cnt}
再对每个环乘上对应的环染色贡献
也就是:
$$ ans = 25^{cnt} \times \prod_{\text{每个环}} \left(25^{len} + (-1)^{len}\cdot 25\right) $$
剩下的问题只有一个:怎么找环?
做法是 拓扑剥叶子 :
统计每个点的入度
把所有入度为 0 的点入队
不断删除这些点,并让它指向的点入度减一
新出现入度为 0 的点继续入队
这样做的原因是:
入度为 0 的点一定不在环上
不断剥掉这些点后,最后剩下的点一定都在环上
于是:
被剥掉的点 = 非环点
没被剥掉的点 = 所有环上的点
再顺着 a[i] 跑一圈,就能求出每个环的长度。
神奇的代码
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 77 78 79 80 81 82 #include <bits/stdc++.h> #define endl '\n' #define int long long const int mod = 998244353 ;int qpower (int y, int x) { int r = 1 ; while (x) { if (x & 1 ) r = r * y % mod; y = y * y % mod; x >>= 1 ; } return r; } void solve () { int n = 0 ; std::cin >> n; std::vector<int > a (n + 1 , 0 ) , in (n + 1 , 0 ) , del (n + 1 , 0 ) , vis (n + 1 , 0 ) ; for (int i = 1 ; i <= n; i++) { std::cin >> a[i]; in[a[i]]++; } std::queue<int > q; for (int i = 1 ; i <= n; i++) { if (in[i] == 0 ) q.push (i); } int cnt = 0 ; while (!q.empty ()) { int u = q.front (); q.pop (); del[u] = 1 ; cnt++; int v = a[u]; in[v]--; if (in[v] == 0 ) q.push (v); } int ans = qpower (25 , cnt); for (int i = 1 ; i <= n; i++) { if (del[i] || vis[i]) continue ; int len = 0 ; int u = i; while (!vis[u]) { vis[u] = 1 ; len++; u = a[u]; } int now = qpower (25 , len); if (len & 1 ) now = (now - 25 + mod) % mod; else now = (now + 25 ) % mod; ans = ans * now % mod; } std::cout << ans % mod << endl; } signed main () { std::ios::sync_with_stdio (false ); std::cin.tie (nullptr ); std::cout.tie (nullptr ); int _t = 1 ; while (_t --) solve (); return 0 ; }