3 条题解
-
0
Answer is here!
#include<bits/stdc++.h> using ll = long long; constexpr ll mod = 998244353; const int maxn = 1e6 + 10; int n, N; ll pw[maxn]; std::vector<int> G[maxn], to[maxn]; int low[maxn], dfn[maxn], dt, stk[maxn], top; int siz[maxn]; void dfs1(int u) { low[u] = dfn[u] = ++dt, stk[++top] = u; for (int v : G[u]) { if (dfn[v]) low[u] = std::min(low[u], dfn[v]); else { dfs1(v), low[u] = std::min(low[u], low[v]); if (dfn[u] == low[v]) { N++, to[u].push_back(n + N), to[n + N].push_back(u); int t; do {t = stk[top--]; to[t].push_back(n + N), to[n + N].push_back(t);} while (t != v); siz[n + N] = to[n + N].size(); } } } } ll f[maxn], g[maxn]; void dfs2(int u, int fa) { if (u != 1 && to[u].size() == 1) return f[u] = g[u] = 1, void(); ll h[3] = {1, 0, 0}; for (int v : to[u]) { if (v == fa) continue; dfs2(v, u); h[2] = (h[2] * (g[v] + 1) + h[1] * g[v]) % mod, h[1] = (h[1] + h[0] * g[v]) % mod; } if (u > n) f[u] = pw[siz[u] - 1] * h[2] % mod, g[u] = (pw[siz[u] - 1] * h[1] + f[u]) % mod; else f[u] = (2ll * h[2] + h[1] + h[0]) % mod, g[u] = (h[1] + f[u]) % mod; } int main() { int m; scanf("%d%d", &n, &m); for (int i = pw[0] = 1; i <= n; i++) (pw[i] = pw[i - 1] << 1) >= mod && (pw[i] -= mod); for (int x, y; m--; ) scanf("%d%d", &x, &y), G[x].push_back(y), G[y].push_back(x); dfs1(1); dfs2(1, 0); ll ans = 0; for (int i = 1; i <= n + N; i++) (ans += f[i]) %= mod; printf("%lld\n", (2ll * ans + 1ll) % mod); return 0; }
thank you!
-
0
怄火。
考虑一下题目条件是在说什么,这个连通性很自然的导向了点双。建出圆方树,那么集合对 是好的当且仅当 的方点邻居或 自己在 的虚树上。
如果我们把 确定下来,那么 的数量必然是 。那么计数的过程中主要维护的对象就应该是 。
对每个集合 ,我们钦定在 虚树的根结点处计数这个集合的贡献。设 表示所有以 为根的虚树的 对应的集合对个数之和。
再令 表示 子树中,在所有 中额外添加一个 ( 到“原本 对应的虚树”的路径,也是虚树的一部分)时,所有 对应的集合对个数之和。
对于方点,我们有转移
$$f_u=2^{s_u-1}\left(\prod_{v\in \operatorname{son}(u)}(1+g_v)-1-\sum_{v\in\operatorname{son}(u)}g_v\right) $$其中 为方点 对应的点双大小。因为钦定了 是虚树的根,那就必须选 个子树。如果选了 个子树,那么任何一个方点 的儿子(圆点)都可以被包含在集合 之中。
不要在这里抉择每个儿子 是否在 里,这里已经在儿子处决择过了。
$$g_u=\left(2^{s_u-1}\sum_{v\in \operatorname{son}(u)}g_v\right)+f_u $$同理,因为到 的路径也是虚树的一部分,方点 的所有儿子(圆点)也可以被包含在 中,所以要乘上 ,最后再加上 。
对于圆点 我们有转移。
$$f_u=\left(\prod_{v\in \operatorname{son}(u)}(1+g_v)-1-\sum_{v\in\operatorname{son}(u)}g_v\right)+\prod_{v\in \operatorname{son}(u)}(1+g_v) $$如果点 不在 中,那么至少要有两个儿子被选择,才能在虚树上。否则无论如何就都是可行的。
$$g_u=\left(\sum_{v\in \operatorname{son}(u)}g_v\right)+f_u $$这个比较显然。
答案就是:
这里乘 是因为,如果 是方点,我们没有决策其父亲是否在 中,如果 是圆点,我们没有决策自己是否在 中。最后加上两者都为空的 。
其中 是圆方树点数。
-
-10
字典序最大路径
思路
本题要求找到一个字典序最大的路径,可以证明答案一定是有限的或者是由某个长度有限的字符串 不断重复得到的。
我们可以使用动态规划来解决这个问题。我们定义 表示从位置 出发,所能得到的字典序最大的路径。
对于每个位置 ,我们可以枚举其四个方向,如果该方向存在且没有障碍,则更新 为该方向的 值加上当前位置的字符。
最后,我们从所有非障碍格出发,找到字典序最大的 值,即为答案。
解题方法
初始化 数组,所有位置的值都设置为 。
从所有非障碍格出发,进行动态规划。
对于每个位置 ,枚举其四个方向,如果该方向存在且没有障碍,则更新 为该方向的 值加上当前位置的字符。
找到所有 值中字典序最大的值,即为答案。
复杂度
时间复杂度:
空间复杂度:
Code
#include <iostream> #include <algorithm> #include <string> using namespace std; const int MAXN = 1005; char grid[MAXN][MAXN]; string dp[MAXN][MAXN]; int n, m; int main() { cin >> n >> m; for (int i = 1; i <= n; i++) { cin >> grid[i] + 1; } // 初始化 dp 数组 for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { dp[i][j] = "-1"; } } // 动态规划 for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { if (grid[i][j] == '#') continue; // 枚举四个方向 if (i > 1 && grid[i - 1][j] != '#') { dp[i][j] = max(dp[i][j], dp[i - 1][j] + grid[i][j]); } if (i < n && grid[i + 1][j] != '#') { dp[i][j] = max(dp[i][j], dp[i + 1][j] + grid[i][j]); } if (j > 1 && grid[i][j - 1] != '#') { dp[i][j] = max(dp[i][j], dp[i][j - 1] + grid[i][j]); } if (j < m && grid[i][j + 1] != '#') { dp[i][j] = max(dp[i][j], dp[i][j + 1] + grid[i][j]); } } } // 找到字典序最大的 dp 值 string ans = "-1"; for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { if (grid[i][j] != '#' && dp[i][j] > ans) { ans = dp[i][j]; } } } cout << ans << endl; return 0;//好习惯 }
最后,本蒟蒻是第一次写题解呦,请大佬们多包涵哟。
- 1
信息
- ID
- 4
- 时间
- 3000ms
- 内存
- 1024MiB
- 难度
- 7
- 标签
- 递交数
- 139
- 已通过
- 25
- 上传者