zoukankan      html  css  js  c++  java
  • Access 题解

    一、题目:

    二、思路:

    这道题真的把我迷了一个下午,感谢 zYzYzYzYz 大佬的点拨,让我终于明白了这道题。

    注意到如果 (y) 的一个祖先是 (x),那么先 (mathbb{access}(x))(mathbb{access}(y)) 本质上相当于只 access 了一次。所以我们可以执行一个从下到上的树形 DP。

    注意,以下这个 DP 状态比较复杂。

    假设我们现在的目标是求出来 (x) 相关的信息,现在轮到了用 (x) 的儿子 (y) 来去更新 (x) 的信息。此时,(tmpf[k]) 表示"保证 (y) 之前的儿子边都不是实边",操作了 (k) 次的方案数;(tmpg[k]) 表示“保证 (y) 之前的儿子边中恰好有一个是实边”,操作了 (k) 次的方案数;(f[x,k]) 表示保证 (y)(y) 之前的儿子边都不是实边,操作了 (k) 次的方案数;(g[x,k]) 表示保证处理完 (y) 之后,(x) 顶上那条边是实边,操作了 (k) 次的方案数。

    则有状态转移方程:

    [egin{aligned} f[x,k]&=sumlimits_{t=0}^{k} tmpf[t] imes f[y,k - t]\ g[x,k]&=sumlimits_{t = 0}^k(tmpg[t] imes f[y,k - t] + tmpf[t] imes g[y,k - t]) end{aligned} ]

    在整个更新完 (x) 的答案之后,我们将 (g[x]) 数组中的值全部赋给 (f[x]),此时 (f[x,k]) 的意义变成 (x) 顶上那条边不是实边,操作了 (k) 次的方案数。为什么可以直接将 (g[x]) 数组中的值赋给 (f[x]) 呢?因为 (x) 顶上的边不是实边,只有可能是 (x) 的父亲用了一次 access,把原本是实边的边变成了虚边。

    最后有一个小细节。就是最后将 (g) 赋给 (f) 的时候,(g[x,0]) 是等于 0 的,而 (g[x,1]) 是大于 0 的。赋给 (f) 了之后,为了保证意义上的自洽以及树的形态不能重复,要将 (f[x,0]gets 1)(f[x,1]gets f[x,1]-1)

    由于 (kleq siz_x),所以这是一个经典的 (O(nK)) 的 DP。

    三、代码:

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <vector>
    
    using namespace std;
    #define FILEIN(s) freopen(s, "r", stdin)
    #define FILEOUT(s) freopen(s, "w", stdout)
    #define mem(s, v) memset(s, v, sizeof s)
    
    inline int read(void) {
        int x = 0, f = 1; char ch = getchar();
        while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
        while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
        return f * x;
    }
    
    const int MAXN = 10005, MOD = 998244353, MAXK = 505;
    
    int n, K, siz[MAXN];
    int head[MAXN], tot;
    long long f[MAXN][MAXK], g[MAXN][MAXK];
    long long tmpf[MAXK], tmpg[MAXK];
    
    struct Edge {
        int y, next;
        Edge() {}
        Edge(int _y, int _next) : y(_y), next(_next) {}
    }e[MAXN << 1];
    
    inline void connect(int x, int y) {
        e[++ tot] = Edge(y, head[x]);
        head[x] = tot;
    }
    
    void dfs(int x, int fa) {
        siz[x] = 1;
        g[x][1] = 1;
        f[x][0] = 1;
        for (int i = head[x]; i; i = e[i].next) {
            int y = e[i].y;
            if (y == fa) continue;
            dfs(y, x);
    
            for (int k = 0; k <= min(K, siz[x]); ++ k) {
                tmpf[k] = f[x][k];
                tmpg[k] = g[x][k];
                f[x][k] = g[x][k] = 0;
            }
    
            for (int j = 0; j <= min(K, siz[x]); ++ j) {
                for (int k = 0; k <= siz[y] && j + k <= K; ++ k) {
                    (f[x][j + k] += tmpf[j] * f[y][k] % MOD) %= MOD;
                    (g[x][j + k] += tmpf[j] * g[y][k] % MOD + tmpg[j] * f[y][k] % MOD) %= MOD;
                }
            }
            siz[x] += siz[y];
        }
        memcpy(f[x], g[x], sizeof f[x]);
        f[x][0] = 1; -- f[x][1];
    }
    
    int main() {
        FILEIN("access.in"); FILEOUT("access.out");
        n = read(); K = read();
        for (int i = 1; i < n; ++ i) {
            int x = read(), y = read();
            connect(x, y); connect(y, x);
        }
        dfs(1, 0);
        long long res = 0;
        for (int i = 0; i <= min(n, K); ++ i) (res += f[1][i]) %= MOD;
        printf("%lld
    ", res);
        return 0;
    }
    
  • 相关阅读:
    强化学习 相关资源
    Log4j输出文件到目的地
    httpclient 封装post 和get
    Cookie 和Session区别
    day09 request 和response
    Jmeter 断言
    Jmeter自学笔记10----性能测试基础实战
    Jmeter 目录
    性能测试解惑之并发压力
    设计模式,就是那个抽象工厂没写
  • 原文地址:https://www.cnblogs.com/little-aztl/p/14961335.html
Copyright © 2011-2022 走看看