3 条题解

  • 0
    @ 2024-10-25 21:46:59

    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
      @ 2024-7-24 19:39:42

      怄火。

      考虑一下题目条件是在说什么,这个连通性很自然的导向了点双。建出圆方树,那么集合对 S,TS,T 是好的当且仅当 SS 的方点邻居或 SS 自己在 TT 的虚树上。

      如果我们把 TT 确定下来,那么 SS 的数量必然是 2T2^{|T|}。那么计数的过程中主要维护的对象就应该是 TT

      对每个集合 TT,我们钦定在 TT 虚树的根结点处计数这个集合的贡献。设 fuf_u 表示所有以 uu 为根的虚树的 TT 对应的集合对个数之和。

      再令 gug_{u} 表示 uu 子树中,在所有 TT 中额外添加一个 uuuu 到“原本 TT 对应的虚树”的路径,也是虚树的一部分)时,所有 TT 对应的集合对个数之和。

      对于方点,我们有转移

      $$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) $$

      其中 sus_u 为方点 uu 对应的点双大小。因为钦定了 uu 是虚树的根,那就必须选 2\ge 2 个子树。如果选了 2\ge 2 个子树,那么任何一个方点 uu 的儿子(圆点)都可以被包含在集合 SS 之中。

      不要在这里抉择每个儿子 vv 是否在 TT 里,这里已经在儿子处决择过了。

      $$g_u=\left(2^{s_u-1}\sum_{v\in \operatorname{son}(u)}g_v\right)+f_u $$

      同理,因为到 uu 的路径也是虚树的一部分,方点 uu 的所有儿子(圆点)也可以被包含在 SS 中,所以要乘上 2su12^{s_u-1},最后再加上 fuf_u

      对于圆点 uu 我们有转移。

      $$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) $$

      如果点 uu 不在 TT 中,那么至少要有两个儿子被选择,才能在虚树上。否则无论如何就都是可行的。

      $$g_u=\left(\sum_{v\in \operatorname{son}(u)}g_v\right)+f_u $$

      这个比较显然。

      答案就是:

      2i=1totfi+12\sum_{i=1}^{tot}f_i+1

      这里乘 22 是因为,如果 uu 是方点,我们没有决策其父亲是否在 SS 中,如果 uu 是圆点,我们没有决策自己是否在 SS 中。最后加上两者都为空的 11

      其中 tottot 是圆方树点数。

      • -10
        @ 2024-7-2 12:46:12

        字典序最大路径

        思路

        本题要求找到一个字典序最大的路径,可以证明答案一定是有限的或者是由某个长度有限的字符串 SSSS 不断重复得到的。

        我们可以使用动态规划来解决这个问题。我们定义 dp[i][j]dp[i][j] 表示从位置 (i,j)(i, j) 出发,所能得到的字典序最大的路径。

        对于每个位置 (i,j)(i, j),我们可以枚举其四个方向,如果该方向存在且没有障碍,则更新 dp[i][j]dp[i][j] 为该方向的 dpdp 值加上当前位置的字符。

        最后,我们从所有非障碍格出发,找到字典序最大的 dpdp 值,即为答案。

        解题方法

        初始化 dpdp 数组,所有位置的值都设置为 1-1

        从所有非障碍格出发,进行动态规划。

        对于每个位置 (i,j)(i, j),枚举其四个方向,如果该方向存在且没有障碍,则更新 dp[i][j]dp[i][j] 为该方向的 dpdp 值加上当前位置的字符。

        找到所有 dpdp 值中字典序最大的值,即为答案。

        复杂度

        时间复杂度:

        𝑂(𝑛×𝑚)𝑂(𝑛×𝑚)

        空间复杂度:

        O(n×m)O(n×m)

        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;//好习惯
        }
        

        最后,本蒟蒻是第一次写题解呦,请大佬们多包涵哟。

        • @ 2024-7-9 18:15:15

          第一次写题解能写错题,你是好样的

        • @ 2024-7-13 18:21:38

          这不是 Luogu P10677 吗

        • @ 2024-10-25 21:46:32

          错的!

      • 1

      信息

      ID
      4
      时间
      3000ms
      内存
      1024MiB
      难度
      7
      标签
      递交数
      139
      已通过
      25
      上传者