@loj - 3044@ 「ZJOI2019」Minimax 搜索


@description@

九条可怜是一个喜欢玩游戏的女孩子。为了增强自己的游戏水平,她想要用理论的武器武装自己。这道题和著名的 Minimax 搜索有关。

可怜有一棵有根树,根节点编号为 1。定义根节点的深度为 1,其他节点的深度为它的父亲的深度加一。同时在叶子节点权值给定的情况下,可怜用如下方式定义了每一个非节点的权值:

对于深度为奇数的非叶子节点,它的权值是它所有子节点的权值最大值。
对于深度为偶数的非叶子节点,它的权值是它所有子节点的权值最小值。

最开始,可怜令编号为 i 的叶子节点权值为 i,并计算得到了根节点的权值为 W。

现在,邪恶的 Cedyks 想要通过修改某些叶子节点的权值,来让根节点的权值发生改变。Cedyks 设计了一个量子攻击器,在攻击器发动后,Cedyks 会随机获得一个非空的叶子节点集合 S 的控制权,并可以花费一定的能量来修改 S 中的叶子节点的权值。

然而,修改叶子节点的权值也要消耗能量,对于 S 中的叶子节点 i,它的初始权值为 i,假设 Cedyks 把它的权值修改成了 wi(wi 可以是任意整数,包括负数),则 Cedyks 在这次攻击中,需要花费的能量为 \(max_{i\in S}|i - w_i|\)

Cedyks 想要尽可能节约能量,于是他总是会以最少的能量来完成攻击,即在花费的能量最小的情况下,让根节点的权值发生改变。令 w(S) 为 Cedyks 在获得了集合 S 的控制权后,会花费的能量。特殊地,对于某些集合 S,可能无论如何改变 S 中叶子节点的权值,根节点的权值都不会发生改变,这时,w(S) 的值被定义为 n。为了方便,我们称 w(S) 为 S 的稳定度。

当有 m 个叶子节点的时候,一共有 2^m-1 种不同的叶子节点的非空集合。在发动攻击前,Cedyks 想要先预估一下自己需要花费的能量。于是他给出了一个区间 [L, R],他想要知道对于每一个 k ∈ [L, R],有多少个集合 S 满足 w(S) = k。

答案可能会很大,请对 998244353 取模后输出。

数据范围与提示
对于 100% 的数据,保证 2 <= n <= 200000, 1 <= L <= R <= n。

@solution@

如果给定稳定度 k,我们可以得到每个叶子所能够更改的权值范围。
但是可能不存在 i 使得 \(max_{i\in S}|i - w_i| = k\),不符合稳定度的定义,
我们求 ans[k] 表示所有 i 都满足 \(max_{i\in S}|i - w_i| \le k\) 的集合数量,最后作个差分。

假设给定叶子集合 S,想要最大可能地改变根的值,必然要把 S 中的叶子取所能够取的最大值与最小值。
设根的权值为 w,如果改成 min 可能导致根权 < w,如果改成 max 可能导致根权 > w。

于是就可以想到一个 dp:定义 dp[0/1][i] 表示 i 这棵子树内有多少叶子集合,将叶子权值改成 min/max 后 i 的权值 w。最后得到 dp[0][rt] + dp[1][rt] 为答案。
但是有一个问题,某些集合可能取 min 也合法,取 max 也合法,就会算重。

注意到改成 min 时,小于 w 的那些叶结点其实根本不用改变(它改变了也没有用,因为它肯定是直接或间接被 w 在某个 max 结点淘汰掉)。
同理,改成 max 时,大于 w 的那些叶结点也不需要改变。

但是对于等于 w 的叶结点,它既可能变大也可能变小。
注意到等于 w 的只有一个,如果一个叶子集合包含 w,它的稳定度必然为 1(直接改 =w 的权值,根结点自然就改变了)。
所以我们可以强制叶子集合不包含 w,最后再加包含 w 的贡献。

我们记 f[x] 表示只把 >w 的改成 min 是否 <w,记 g[x] 表示只把 w。可以作两次 dp 求出 f,g。
最后可以根据 w 的叶子数量算出答案。

每一次 dp 都是 O(n) 复杂度,可是我们要求 [L, R] 中的所有 dp 值。
注意 i 与 i+1 对应的情况只有最多两个叶子会改变,直接动态 dp 即可。

@accepted code@

#include <cstdio>
#include <algorithm>
using namespace std;
const int MAXN = 200000;
const int MOD = 998244353;
struct mint{
    int x;
    mint(int _x=0) : x(_x) {}
    friend mint operator + (mint a, mint b) {return a.x + b.x >= MOD ? a.x + b.x - MOD : a.x + b.x;}
    friend mint operator - (mint a, mint b) {return a.x - b.x < 0 ? a.x - b.x + MOD : a.x - b.x;}
    friend mint operator * (mint a, mint b) {return 1LL * a.x * b.x % MOD;}
    friend void operator *= (mint &a, mint b) {a = a * b;}
    friend void operator /= (mint &a, mint b) {
        int p = MOD - 2;
        while( p ) {
            if( p & 1 ) a *= b;
            b *= b;
            p >>= 1;
        }
    }
};
struct matrix{
    mint m[3][3];
    matrix() {
        for(int i=0;i<3;i++)
            for(int j=0;j<3;j++)
                m[i][j] = 0;
    }
    friend matrix operator * (matrix A, matrix B) {
        matrix C;
        C.m[0][0] = A.m[0][0]*B.m[0][0] + A.m[0][1]*B.m[1][0] + A.m[0][2]*B.m[2][0];
        C.m[0][1] = A.m[0][0]*B.m[0][1] + A.m[0][1]*B.m[1][1] + A.m[0][2]*B.m[2][1];
        C.m[0][2] = A.m[0][0]*B.m[0][2] + A.m[0][1]*B.m[1][2] + A.m[0][2]*B.m[2][2];
        C.m[1][0] = A.m[1][0]*B.m[0][0] + A.m[1][1]*B.m[1][0] + A.m[1][2]*B.m[2][0];
        C.m[1][1] = A.m[1][0]*B.m[0][1] + A.m[1][1]*B.m[1][1] + A.m[1][2]*B.m[2][1];
        C.m[1][2] = A.m[1][0]*B.m[0][2] + A.m[1][1]*B.m[1][2] + A.m[1][2]*B.m[2][2];
        C.m[2][0] = A.m[2][0]*B.m[0][0] + A.m[2][1]*B.m[1][0] + A.m[2][2]*B.m[2][0];
        C.m[2][1] = A.m[2][0]*B.m[0][1] + A.m[2][1]*B.m[1][1] + A.m[2][2]*B.m[2][1];
        C.m[2][2] = A.m[2][0]*B.m[0][2] + A.m[2][1]*B.m[1][2] + A.m[2][2]*B.m[2][2];
        return C;
    }
}I;
mint pw2[MAXN + 5];
void init() {
    pw2[0] = 1;
    for(int i=1;i<=MAXN;i++)
        pw2[i] = 2*pw2[i-1];
    I.m[0][0] = I.m[1][1] = I.m[2][2] = 1;
}
struct edge{
    int to; edge *nxt;
}edges[2*MAXN + 5], *adj[MAXN + 5], *ecnt = edges;
void addedge(int u, int v) {
    edge *p = (++ecnt);
    p->to = v, p->nxt = adj[u], adj[u] = p;
    p = (++ecnt);
    p->to = u, p->nxt = adj[v], adj[v] = p;
}
int val[MAXN + 5], dep[MAXN + 5];
int fa[MAXN + 5], siz[MAXN + 5], hvy[MAXN + 5];
void dfs1(int x, int f) {
    fa[x] = f, siz[x] = 1, hvy[x] = 0, dep[x] = dep[f] + 1;
    for(edge *p=adj[x];p;p=p->nxt) {
        if( p->to == f ) continue;
        dfs1(p->to, x), siz[x] += siz[p->to];
        val[x] = (val[x] ? ((dep[x] & 1) ? max(val[x], val[p->to]) : min(val[x], val[p->to])) : val[p->to]);
        if( siz[hvy[x]] < siz[p->to] )
            hvy[x] = p->to;
    }
    if( !hvy[x] ) val[x] = x;
}
struct node{
    int z; mint x;
    node(int _x=0) : z(0), x(_x) {}
    friend void operator *= (node &a, mint b) {
        if( b.x == 0 ) a.z++;
        else a.x *= b;
    }
    friend void operator /= (node &a, mint b) {
        if( b.x == 0 ) a.z--;
        else a.x /= b;
    }
    mint key() {return z ? 0 : x;}
}a[MAXN + 5], b[MAXN + 5];
mint f[MAXN + 5], g[MAXN + 5];
/*
f -> (only modify leaf > v1, vi < w)
g -> (only modify leaf < v1, vi > w)
*/
int lc1[MAXN + 5], lc2[MAXN + 5]; 
int top[MAXN + 5], dfn[MAXN + 5], tid[MAXN + 5], btm[MAXN + 5], dcnt;
int d;
int ff(int x) {return (x > val[1] && x - d < val[1]) + (x < val[1]);}
int gg(int x) {return (x < val[1] && x + d > val[1]) + (x > val[1]);}
//选入集合 + 不选入集合
int dfs2(int x, int tp) {
    top[x] = tp, dfn[++dcnt] = x, tid[x] = dcnt;
    if( !hvy[x] ) {
/*
        f[x] = (x > val[1] && x - 0 < val[1]) + (x < val[1]);
        g[x] = (x < val[1] && x + 0 > val[1]) + (x > val[1]);
*/
        f[x] = ff(x), g[x] = gg(x);
        lc1[x] += (x > val[1]), lc2[x] += (x < val[1]);
        return btm[x] = x;
    }
    btm[x] = dfs2(hvy[x], tp), lc1[x] += lc1[hvy[x]], lc2[x] += lc2[hvy[x]];
    for(edge *p=adj[x];p;p=p->nxt)
        if( p->to != hvy[x] && p->to != fa[x] )
            dfs2(p->to, p->to), lc1[x] += lc1[p->to], lc2[x] += lc2[p->to];
    if( dep[x] & 1 ) {
        a[x] = b[x] = 1;
        for(edge *p=adj[x];p;p=p->nxt)
            if( p->to != hvy[x] && p->to != fa[x] )
                a[x] *= f[p->to], b[x] *= (pw2[lc2[p->to]] - g[p->to]);
        f[x] = a[x].key()*f[hvy[x]];
        g[x] = pw2[lc2[x]] - b[x].key()*pw2[lc2[hvy[x]]] + b[x].key()*g[hvy[x]];
    }
    else {
        a[x] = b[x] = 1;
        for(edge *p=adj[x];p;p=p->nxt)
            if( p->to != hvy[x] && p->to != fa[x] )
                a[x] *= g[p->to], b[x] *= (pw2[lc1[p->to]] - f[p->to]);
        g[x] = a[x].key()*g[hvy[x]];
        f[x] = pw2[lc1[x]] - b[x].key()*pw2[lc1[hvy[x]]] + b[x].key()*f[hvy[x]];
    }
    return btm[x];
}
matrix getM(int x) {
    if( !hvy[x] ) return I;
    matrix M;
    if( dep[x] & 1 ) {
        M.m[0][0] = a[x].key(), M.m[1][1] = b[x].key();
        M.m[1][2] = pw2[lc2[x]] - b[x].key()*pw2[lc2[hvy[x]]];
        M.m[2][2] = 1;
    }
    else {
        M.m[1][1] = a[x].key(), M.m[0][0] = b[x].key();
        M.m[0][2] = pw2[lc1[x]] - b[x].key()*pw2[lc1[hvy[x]]];
        M.m[2][2] = 1;
    }
    return M;
}
struct segtree{
    #define lch (x << 1)
    #define rch (x << 1 | 1)
    struct node{
        int le, ri;
        matrix M;
    }t[4*MAXN + 5];
    void pushup(int x) {t[x].M = t[lch].M * t[rch].M;}
    void build(int x, int l, int r) {
        t[x].le = l, t[x].ri = r;
        if( l == r ) {
            t[x].M = getM(dfn[l]);
            return ;
        }
        int m = (l + r) >> 1;
        build(lch, l, m), build(rch, m + 1, r);
        pushup(x);
    }
    void update(int x, int p) {
        if( p > t[x].ri || p < t[x].le )
            return ;
        if( t[x].le == t[x].ri ) {
            t[x].M = getM(dfn[p]);
            return ;
        }
        update(lch, p), update(rch, p);
        pushup(x);
    }
    matrix query(int x, int l, int r) {
        if( l > t[x].ri || r < t[x].le )
            return I;
        if( l <= t[x].le && t[x].ri <= r )
            return t[x].M;
        return query(lch, l, r) * query(rch, l, r);
    }
}T;
void modify(int x) {
    while( fa[top[x]] ) {
        x = top[x];
        if( dep[fa[x]] & 1 )
            a[fa[x]] /= f[x], b[fa[x]] /= (pw2[lc2[x]] - g[x]);
        else a[fa[x]] /= g[x], b[fa[x]] /= (pw2[lc1[x]] - f[x]);
        matrix M = T.query(1, tid[x], tid[btm[x]]);
        f[x] = M.m[0][0]*ff(btm[x]) + M.m[0][2];
        g[x] = M.m[1][1]*gg(btm[x]) + M.m[1][2];
        if( dep[fa[x]] & 1 )
            a[fa[x]] *= f[x], b[fa[x]] *= (pw2[lc2[x]] - g[x]);
        else a[fa[x]] *= g[x], b[fa[x]] *= (pw2[lc1[x]] - f[x]);
        x = fa[x];
        T.update(1, tid[x]);
    }
}
mint get_ans() {
    matrix M = T.query(1, tid[1], tid[btm[1]]);
    mint f1 = M.m[0][0]*ff(btm[1]) + M.m[0][2];
    mint g1 = M.m[1][1]*gg(btm[1]) + M.m[1][2];
    return pw2[lc1[1] + lc2[1]] - (pw2[lc1[1]] - f1) * (pw2[lc2[1]] - g1);
}
int n, L, R;
mint ans[MAXN + 5];
int read() {
    int x = 0; char ch = getchar();
    while( ch > '9' || ch < '0' ) ch = getchar();
    while( '0' <= ch && ch <= '9' ) x = 10*x + ch - '0', ch = getchar();
    return x;
}
void write(int x) {
    if( !x ) return ;
    write(x/10);
    putchar(x%10 + '0');
}
int main() {
    init(), n = read(), L = read(), R = read();
    for(int i=1;i<n;i++) {
        int u = read(), v = read();
        addedge(u, v);
    }
    dfs1(1, 0), dfs2(1, 1), T.build(1, 1, n);
    for(d=1;d<=n;d++) {
        int p = (val[1] + 1 - d);
        if( p < val[1] && p >= 1 && !hvy[p] )
            modify(p);
        p = (val[1] - 1 + d);
        if( p > val[1] && p <= n && !hvy[p] )
            modify(p);
        ans[d] = get_ans();
    }
    ans[n] = pw2[lc1[1]+lc2[1]] - ans[n] - 1;
    for(int i=n-1;i>=1;i--) ans[i] = ans[i] - ans[i-1];
    ans[1] = ans[1] + pw2[lc1[1]+lc2[1]];
    for(int i=L;i<=R;i++) {
        if( ans[i].x ) write(ans[i].x); else putchar('0');
        putchar(i == n ? '\n' : ' ');
    }
}

@details@

所以我并不知道为什么本地测大样例要跑 5s 交上去竟然 AC 了。

猜你喜欢

转载自www.cnblogs.com/Tiw-Air-OAO/p/11915347.html