[LOJ#3044][动态DP]「ZJOI2019」Minimax 搜索

  • 题目传送门

  • 容易想到一种暴力 DP:先转化成对于每个 \(k\) 求出 \(\max_{i\in S}|i-w_i|\le k\) 的方案数,最后差分

  • 然后问题转化成每个叶子的权值有个取值区间,注意这时我们可以把每个点的权值分成 \(<W,=W,>W\)(看作 \(0,1,2\) )三类处理,然后 DP \(f[u][0/1/2]\)

  • 然后你会很快发现这么做不行,因为选取某些叶子集合时,根节点的权值可以小于 \(W\) 也可以大于 \(W\) ,直接用 \(2^m-1\) 减掉 \(f[u][0]+f[u][2]\) 无法得到正确结果

  • 于是我们尝试考虑怎样才能在 \(<W\)\(>W\) 这两个条件中去掉一个

  • 考虑权值为 \(W\) 的叶子到根的一条链

  • 容易发现根节点的权值会被改变,当且仅当这条链上存在一个点会被改变

  • 设这条链上有一个深度为奇数的点 \(u\) (偶数同理),考虑把链上所有的边都删掉之后 \(u\) 所在的连通块

  • 易得在原树上 \(u\) 的权值会被改,当且仅当删边之后\(u\) 所在的连通块,能让 \(u\) 的权值大于 \(W\)

  • 特殊地,如果 \(u\) 是叶子那么 \(u\) 的权值会被改当且仅当 \(u\) 在选定集合内且 \(k>0\)

  • 这样我们就实现了在 \(<W\)\(>W\) 这两个条件中去掉一个

  • 我们有了一个 DP:\(f[u]\) 表示 \(u\) 的子树内的叶子节点有多少个子集能让 \(u\) 的权值大于 \(W\)

  • 如果 \(u\) 的深度为奇数(\(cnt_u\)\(u\) 的子树内叶子个数):

  • \[f[u]=2^{cnt_u}-\prod_{v\in son[u]}(2^{cnt_v}-f[v])\]

  • 否则:

  • \[f[u]=\prod_{v\in son[u]}f[v]\]

  • 把所有连通块的 DP 结果用 \(2^{cnt}\) 减掉后乘起来,即为根节点权值不会被改变的方案数

  • 对每个 \(k\) 进行 DP 的复杂度为 \(O(n^2)\)

  • 考虑如何优化。我们注意到这个 DP 的转移和 \(k\) 无关,只有初始值(叶子)和 \(k\) 有关

  • 而如果一个点 \(u\) 所在的连通块根在原树中的深度为奇数,那么点 \(u\) 的初值应该为:

  • \[f[u]=[u>W]+[u+k>W]\]

  • 注意到当 \(k\) 不断加一的时候, \([u+k>W]\) 只会改变一次

  • 于是从小到大枚举 \(k\) 后动态 DP 即可

  • 注意由于 \(f[u]\) 可以为 \(0\) ,所以往上更新 DP 值时不能把 \(f[fa[u]]\) 直接除以 \(f[u]\) ,而需要对每个点维护一个变量表示子节点的 DP 值中有多少个 \(0\) ,另一个变量表示子节点不为 \(0\) 的 DP 值之积

  • \(O(n\log^2n)\)

Code

#include <bits/stdc++.h>
#define p2 p << 1
#define p3 p << 1 | 1

template <class T>
inline void read(T &res)
{
    res = 0; bool bo = 0; char c;
    while (((c = getchar()) < '0' || c > '9') && c != '-');
    if (c == '-') bo = 1; else res = c - 48;
    while ((c = getchar()) >= '0' && c <= '9')
        res = (res << 3) + (res << 1) + (c - 48);
    if (bo) res = ~res + 1;
}

template <class T>
inline T Min(const T &a, const T &b) {return a < b ? a : b;}

template <class T>
inline T Max(const T &a, const T &b) {return a > b ? a : b;}

const int N = 2e5 + 5, M = N << 1, L = M << 1, rqy = 998244353;

int n, l, r, dep[N], ecnt, nxt[M], adj[N], go[M], val[N], ans[N], f[N], cnt[N],
fa[N], sze[N], son[N], top[N], pos[N], idx[N], bot[N], QAQ, pw[N], re[N], c0[N],
rea = 1, cnt0;
bool lea[N], bel[N];

int qpow(int a, int b)
{
    int res = 1;
    while (b)
    {
        if (b & 1) res = 1ll * res * a % rqy;
        a = 1ll * a * a % rqy;
        b >>= 1;
    }
    return res;
}

struct modi
{
    int a, b;
    
    friend inline modi operator * (modi x, modi y)
    {
        return (modi) {(int) (1ll * x.a * y.a % rqy),
            (int) ((1ll * x.a * y.b + x.b) % rqy)};
    }
    
    friend inline int operator * (modi x, int y)
    {
        return (1ll * x.a * y + x.b) % rqy;
    }
} g[N], gp[L];

void add_edge(int u, int v)
{
    nxt[++ecnt] = adj[u]; adj[u] = ecnt; go[ecnt] = v;
    nxt[++ecnt] = adj[v]; adj[v] = ecnt; go[ecnt] = u;
}

void dfs(int u, int fu)
{
    dep[u] = dep[fu] + 1;
    val[u] = dep[u] & 1 ? 1 : n;
    bool is = 1;
    for (int e = adj[u], v; e; e = nxt[e])
        if ((v = go[e]) != fu)
        {
            dfs(v, u); is = 0;
            if (dep[u] & 1) val[u] = Max(val[u], val[v]);
            else val[u] = Min(val[u], val[v]);
        }
    if (is) lea[val[u] = u] = 1, ans[0] = (ans[0] + ans[0]) % rqy;
}

void dfs1(int u, int fu, bool op)
{
    fa[u] = fu; sze[u] = 1; bel[u] = op; cnt[u] = lea[u];
    for (int e = adj[u], v; e; e = nxt[e])
        if ((v = go[e]) != fu && val[v] != val[1])
        {
            dfs1(v, u, op);
            sze[u] += sze[v]; cnt[u] += cnt[v];
            if (sze[v] > sze[son[u]]) son[u] = v;
        }
    f[u] = (op ? val[u] > val[1] : val[u] < val[1]) ? pw[cnt[u]] : 0;
    g[u].a = re[u] = 1;
    if ((dep[u] & 1) ^ op)
    {
        for (int e = adj[u], v; e; e = nxt[e])
            if ((v = go[e]) != fu && val[v] != val[1] && v != son[u])
            {
                g[u].a = 1ll * g[u].a * f[v] % rqy;
                if (f[v]) re[u] = 1ll * re[u] * f[v] % rqy; else c0[u]++;
            }
    }
    else
    {
        for (int e = adj[u], v; e; e = nxt[e])
            if ((v = go[e]) != fu && val[v] != val[1] && v != son[u])
            {
                int tmp = (pw[cnt[v]] - f[v] + rqy) % rqy;
                g[u].a = 1ll * g[u].a * tmp % rqy;
                if (tmp) re[u] = 1ll * re[u] * tmp % rqy; else c0[u]++;
            }
        g[u].b = (pw[cnt[u]] - 1ll * g[u].a * pw[cnt[son[u]]] % rqy + rqy) % rqy;
    }
    bot[u] = son[u] ? bot[son[u]] : u;
}

void dfs2(int u, int fu)
{
    if (son[u])
    {
        top[son[u]] = top[u];
        idx[pos[son[u]] = ++QAQ] = son[u];
        dfs2(son[u], u);
    }
    for (int e = adj[u], v; e; e = nxt[e])
        if ((v = go[e]) != fu && v != son[u] && val[v] != val[1])
            top[v] = v, idx[pos[v] = ++QAQ] = v, dfs2(v, u);
}

void build(int l, int r, int p)
{
    if (l == r) return (void) (gp[p] = g[idx[l]]);
    int mid = l + r >> 1;
    build(l, mid, p2); build(mid + 1, r, p3);
    gp[p] = gp[p2] * gp[p3];
}

void change(int l, int r, int pos, modi v, int p)
{
    if (l == r) return (void) (gp[p] = v);
    int mid = l + r >> 1;
    if (pos <= mid) change(l, mid, pos, v, p2);
    else change(mid + 1, r, pos, v, p3);
    gp[p] = gp[p2] * gp[p3];
}

modi ask(int l, int r, int s, int e, int p)
{
    if (e < l || s > r) return (modi) {1, 0};
    if (s <= l && r <= e) return gp[p];
    int mid = l + r >> 1;
    return ask(l, mid, s, e, p2) * ask(mid + 1, r, s, e, p3);
}

void modify(int u, int x)
{
    bool fi = 1; int of;
    while (1)
    {
        int v = top[u], w = fa[v], rf = f[v], nf;
        if (fi) fi = 0, f[u] = x; nf = f[v];
        if (v != bot[u]) nf = f[v] =
            ask(1, n, pos[v], pos[bot[u]] - 1, 1) * f[bot[u]] % rqy;
        if (!w) {of = rf; u = v; break;}
        if ((dep[v] & 1) ^ bel[v]) rf = (pw[cnt[v]] - rf + rqy) % rqy,
            nf = (pw[cnt[v]] - nf + rqy) % rqy;
        if (!rf) c0[w]--; else re[w] = 1ll * re[w] * qpow(rf, rqy - 2) % rqy;
        if (!nf) c0[w]++; else re[w] = 1ll * re[w] * nf % rqy;
        g[w].a = c0[w] ? 0 : re[w];
        g[w].b = (dep[w] & 1) ^ bel[w] ? 0 : (pw[cnt[w]] -
            1ll * g[w].a * pw[cnt[son[w]]] % rqy + rqy) % rqy;
        change(1, n, pos[w], g[w], 1);
        u = w;
    }
    if (of == pw[cnt[u]]) cnt0--; else rea = 1ll * rea *
        qpow(pw[cnt[u]] - of + rqy, rqy - 2) % rqy;
    if (f[u] == pw[cnt[u]]) cnt0++;
        else rea = 1ll * rea * (pw[cnt[u]] - f[u] + rqy) % rqy;
}

int main()
{
    int x, y;
    read(n); read(l); read(r);
    for (int i = 1; i < n; i++)
        read(x), read(y), add_edge(x, y);
    ans[0] = pw[0] = ans[n] = 1; dfs(1, 0);
    for (int i = 1; i <= n; i++) pw[i] = (pw[i - 1] + pw[i - 1]) % rqy;
    rea = 499122177ll * ans[0] % rqy;
    for (int u = 1; u <= n; u++)
    {
        if (val[u] != val[1] || u == val[1]) continue;
        dfs1(u, 0, dep[u] & 1);
        if (!cnt[u] || lea[u]) continue;
        idx[pos[u] = ++QAQ] = top[u] = u;
        dfs2(u, 0);
    }
    build(1, n, 1);
    for (int i = 1; i < n; i++)
    {
        if (i > 1 && val[1] >= i && lea[val[1] - i + 1] && bel[val[1] - i + 1])
            modify(val[1] - i + 1, 1);
        if (i > 1 && val[1] <= n - i + 1 && lea[val[1] + i - 1]
            && !bel[val[1] + i - 1]) modify(val[1] + i - 1, 1);
        ans[i] = cnt0 ? 0 : rea;
    }
    for (int i = l; i <= r; i++) printf("%d ", (ans[i - 1] - ans[i] + rqy) % rqy);
    return puts(""), 0;
}

猜你喜欢

转载自www.cnblogs.com/xyz32768/p/12040207.html
今日推荐