2022.11.16

上午打模拟赛,用线段树结果被疯狂卡常,人已经被卡没了。

对,人没了。

模拟赛 T1 弱化版

补题

Cover

状态:
\(\large{f_{i,j,k}(k\in \left(0,1,2\right))}\) 表示 \(i\) 子树中(不考虑 \(i\) 的父节点),有 \(j\) 个节点被覆盖,状态为 \(k\)

  • \(k = 0\):节点 \(i\) 和节点 \(i\) 的所有子节点都没有被选择;
  • \(k = 1\):节点 \(i\) 被选择;
  • \(k = 2\):节点 \(i\) 没有被选择,但有 \(i\) 的子节点被选择。

转移的话不如直接放代码,方便一点(其实就是懒)。
不要被展开的赫鲁晓夫楼吓到了,本来可以用 for 枚举,但为了更具体地解释还是展开了

code
int size[N];
int f[N][N][3];
int tmp[N][3];// tmp用于转移时临时存储,避免重复更新
void DP(int u, int fa){
    size[u] = 1;
    f[u][0][0] = 1;
    f[u][1][1] = 1;
    for(int i = head[u]; i; i = e[i].nextt){
        int v = e[i].to;
        if(v == fa)continue;
        DP(v, u);
        memset(tmp, 0, sizeof(tmp));
        for(int j = 0; j <= size[u]; ++j){
            for(int k = 0; k <= size[v]; ++k){
                ad(tmp[j + k][0], 1ll * f[u][j][0] * f[v][k][0] % mod);
                // f[u][j][0]之前没有子节点被选,f[v][k][0]这个子节点也不选。
                ad(tmp[j + k][0], 1ll * f[u][j][0] * f[v][k][2] % mod);
                // f[v][k][2]这个子节点也不选,但被覆盖过。
                
                ad(tmp[j + k + 1][1], 1ll * f[u][j][1] * f[v][k][0] % mod);
                // f[u][j][1]u被选了,f[v][k][0]这个子节点不选且没有被覆盖。
                // !!注意,因为v没有被覆盖而u选了,u为根的树下就会多一个v被覆盖,所以是j+k+1。
                ad(tmp[j + k][1], 1ll * f[u][j][1] * f[v][k][1] % mod);
                // f[u][j][1]u被选了,f[v][k][1]v也被选了。
                ad(tmp[j + k][1], 1ll * f[u][j][1] * f[v][k][2] % mod);
                // f[u][j][1]u被选了,f[v][k][2]v没被选,但被覆盖了。

                // [2]的转移比较重点。

                ad(tmp[j + k + 1][2], 1ll * f[u][j][0] * f[v][k][1] % mod);
                // f[u][j][0]之前的子节点没有覆盖过u,f[v][k][1]当前的v成了第一个覆盖u的,在这里j+k+1。
                ad(tmp[j + k][2], 1ll * f[u][j][2] * f[v][k][0] % mod);
                ad(tmp[j + k][2], 1ll * f[u][j][2] * f[v][k][1] % mod);
                ad(tmp[j + k][2], 1ll * f[u][j][2] * f[v][k][2] % mod);
                // f[u][j][2]之前的子节点覆盖过u,f[v][k]可以随便选。
            }
        }
        size[u] += size[v];
        for(int j = 0; j <= size[u]; ++j)
            for(int l = 0; l <= 2; ++l)
                f[u][j][l] = tmp[j][l];// 还给f。
    }
    return ;
}

注意这里 \(size\) 的写法,这样写可以把 \(\Theta(n^3)\) 优化成 \(\Theta(n^2)\)
证明:枚举 \(j\) 在时间上相当于把 \(v\) 之前的所有子树里的节点全部枚举一遍,这时再枚举一遍 \(v\) 的节点,在时间上相当于把 \(v\) 之前的子树里的节点和 \(v\) 子树里的节点枚举配对,这时我们发现对于任意两点,它们只会在 LCA 处被配对一次,这样的时间复杂度就一定是 \(\Theta(n^2)\) 的了。

posted @ 2022-11-16 20:24  Cotsheep  阅读(17)  评论(0编辑  收藏  举报