zoukankan      html  css  js  c++  java
  • LOJ 2537 「PKUWC2018」Minimax

    BZOJ 5461。

    线段树合并优化$dp$。

    假设所有离散之后的权值$in [1, m]$,对于一个点$x$它的权值是$i$的概率是$f(x, i)$,那么

    1、假如这个点只有一个儿子$y$,那么$f(x, i) = f(y, i)$。

    2、假如这个点有两个儿子$y, z$,那么

    $$f(x, i) = f(y, i)sum_{j = 1}^{m}f(z, j)(p_x[i geq j] + (1 - p_x)[i leq j]) + f(z, i)sum_{j = 1}^{m}f(y, j)(p_x[i geq j] + (1 - p_x)[i leq j])$$

    看上去$f(y, i) * f(z, i)$这一项被算了两次,但是题目保证了所有权值互不相同,所以似乎并没有问题。

    $$f(x, i) = f(y, i)(p_xsum_{j = 1}^{i}f(z, j) + (1 - p_x)sum_{j = i}^{m}f(z, j)) + f(z, i)(p_xsum_{j = 1}^{i}f(y, j) + (1 - p_x)sum_{j = i}^{m}f(y, j))$$

    这样子就把$f(x, i)$表示成了$f(y, i)$乘上$f(z)$的贡献加上$f(z, i)$乘上$f(y)$的贡献,对于一个特定的$x$,$p_x$是一个定值,只要在线段树合并的时候算出向下的贡献累加到结点中即可。

    注意到当合并的时候会出现一个结点是空结点的情况,这时候其实相当于整段区间乘上了一段定值,可以采用在线段树上打标记的形式来维护。

    以后线段树合并都直接合并到原来的结点上去,不要开新节点,这样子好写而且空间小,注意合并之后原来的权值可能会发生变化,所以开一个变量把原来的值记录下来。

    时间复杂度$O(nlogn)$。

    Code:

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    
    const int N = 3e5 + 5;
    const int M = 1e7 + 5;
    const ll P = 998244353LL;
    
    int n, fa[N], son[N][3], m = 0;
    ll a[N], buc[N];
    bool isLeaf[N];
    
    template <typename T>
    inline void read(T &X) {
        X = 0; char ch = 0; T op = 1;
        for (; ch > '9'|| ch < '0'; ch = getchar())
            if (ch == '-') op = -1;
        for (; ch >= '0' && ch <= '9'; ch = getchar())
            X = (X << 3) + (X << 1) + ch - 48;
        X *= op;
    }
    
    template <typename T>
    inline void inc(T &x, T y) {
        x += y;
        if (x >= P) x -= P;
    }
    
    template <typename T>
    inline void sub(T &x, T y) {
        x -= y;
        if (x < 0) x += P;
    }
    
    inline ll fpow(ll x, ll y) {
        ll res = 1;
        for (x %= P; y > 0; y >>= 1) {
            if (y & 1) res = res * x % P;
            x = x * x % P;
        }
        return res;
    }
    
    void dfs(int x) {
        isLeaf[x] = 1;
        for (int i = 1; i <= son[x][0]; i++) {
            isLeaf[x] = 0;
            dfs(son[x][i]);
        }
    }
    
    namespace SegT {
        struct Node {
            int lc, rc;
            ll sum, tag;
        } s[M];
        
        #define lc(p) s[p].lc
        #define rc(p) s[p].rc
        #define sum(p) s[p].sum
        #define tag(p) s[p].tag
        #define mid ((l + r) >> 1)
        
        int root[N], nodeCnt = 0;
            
        inline void down(int p) {
            if (tag(p) == 1LL) return;
            if (lc(p)) {
                (sum(lc(p)) *= tag(p)) %= P;
                (tag(lc(p)) *= tag(p)) %= P;        
            }
            if (rc(p)) {
                (sum(rc(p)) *= tag(p)) %= P;
                (tag(rc(p)) *= tag(p)) %= P;            
            }
            tag(p) = 1;
        }
        
        void ins(int &p, int l, int r, int x) {
            p = ++nodeCnt, tag(p) = sum(p) = 1;
            if (l == r) return;
            
            if (x <= mid) ins(lc(p), l, mid, x);
            else ins(rc(p), mid + 1, r, x);
        }
        
        int merge(int u, int v, ll su, ll sv, ll k) {        
            if (!u) {
                (tag(v) *= su) %= P;
                (sum(v) *= su) %= P;
                return v;
            }
            if (!v) {
                (tag(u) *= sv) %= P;
                (sum(u) *= sv) %= P;
                return u;            
            }
                    
            down(u), down(v);
            ll k1 = k, k2 = 1, rsu = sum(rc(u)), lsu = sum(lc(u)), rsv = sum(rc(v)), lsv = sum(lc(v));
            sub(k2, k);
            
            lc(u) = merge(lc(u), lc(v), (su + k2 * rsu % P) % P, (sv + k2 * rsv % P) % P, k);
            rc(u) = merge(rc(u), rc(v), (su + k1 * lsu % P) % P, (sv + k1 * lsv % P) % P, k);
            
            sum(u) = 0;
            inc(sum(u), sum(lc(u))), inc(sum(u), sum(rc(u)));
            return u;
        }
        
        ll query(int p, int l, int r, int x) {
            if (l == r) return sum(p);
            
            down(p);
            if (x <= mid) return query(lc(p), l, mid, x);
            else return query(rc(p), mid + 1, r, x); 
        }    
        
        void deb(int rt) {
            for (int i = 1; i <= m; i++)
                printf("%lld%c", query(rt, 1, m, i), i == m ? '
    ' : ' ');
        }
        
    } using namespace SegT;
    
    void solve(int x) {    
        if (isLeaf[x]) return;
        
        for (int i = 1; i <= son[x][0]; i++) solve(son[x][i]);
        
        if (son[x][0] == 1) root[x] = root[son[x][1]];
        if (son[x][0] == 2) root[x] = SegT :: merge(root[son[x][1]], root[son[x][2]], 0, 0, a[x] % P);
    }
    
    int main() {
        #ifndef ONLINE_JUDGE
            freopen("5.in", "r", stdin);
        #endif
    
        read(n);
        for (int i = 1; i <= n; i++) {
            read(fa[i]);
            if (fa[i] != 0) son[fa[i]][++son[fa[i]][0]] = i;
        }
        dfs(1);
        
        ll inv10000 = fpow(10000LL, P - 2);
        for (int i = 1; i <= n; i++) {
            read(a[i]);
            if (isLeaf[i]) buc[++m] = a[i];
            else a[i] = a[i] * inv10000 % P;
        }
        
        sort(buc + 1, buc + 1 + m);
        for (int i = 1; i <= n; i++) {
            if (!isLeaf[i]) continue;
            a[i] = lower_bound(buc + 1, buc + 1 + m, a[i]) - buc;
            ins(root[i], 1, m, a[i]);
        }
            
        solve(1);
        
        ll ans = 0;
        for (int i = 1; i <= m; i++) {
            ll p = query(root[1], 1, m, i);    
            inc(ans, 1LL * i * p % P * p % P * (buc[i] % P) % P);
        }
        
        printf("%lld
    ", ans);
        
    /*    for (int i = 1; i <= m; i++) {
            ll p = query(root[1], 1, m, i);
            printf("%lld%c", p, i == m ? '
    ' : ' ');
        }   */
        return 0;
    }
    View Code
  • 相关阅读:
    二叉树(8)----第一个二叉树K层节点和二进制部分K叶节点层,递归和非递归
    android文件缓存,并SD卡创建目录未能解决和bitmap内存溢出解决
    Ubuntu Ruby On Rails安装和配置
    Atitit.升级软件的稳定性---基于数据库实现持久化 循环队列 循环队列
    ORACLE经常使用的命令
    增加 Java 有几个好习惯表现
    HTML5学习资源
    FFT 的C 语言
    MongoDB在window下的安装
    白话经典算法系列之七 堆与堆排序
  • 原文地址:https://www.cnblogs.com/CzxingcHen/p/10270677.html
Copyright © 2011-2022 走看看