zoukankan      html  css  js  c++  java
  • LOJ2537. 「PKUWC2018」Minimax【概率DP+线段树合并】

    LINK


    思路

    首先暴力(n^2)是很好想的,就是把当前节点概率按照权值大小做前缀和和后缀和然后对于每一个值直接在另一个子树里面算出贡献和就可以了,注意乘上选最大的概率是小于当前权值的部分,选最小是大于当前权值的部分

    然后考虑怎么优化

    用线段树合并来做

    每次向左递归的时候就把x右子树对y左子树的贡献加上,把y右子树对x左子树的贡献加上

    每次向左递归的时候就把x左子树对y右子树的贡献加上,把y左子树对x右子树的贡献加上

    考虑每个节点,左边的区间贡献一定会被统计完全,右边的区间贡献一定会被统计完全

    然后这样统计下来所有的点都是被更新了的

    所以只需要动态开点线段树维护一个区间和和区间乘标记就可以了

    然后就做完了


    注意dfs的边界,如果只有一个儿子就可以直接赋值了


    #include<bits/stdc++.h>
    
    using namespace std;
    
    typedef long long ll;
    
    namespace io {
    
    const int BUFSIZE = 1 << 20;
    char ibuf[BUFSIZE], *is = ibuf, *it = ibuf;
    char obuf[BUFSIZE], *os = obuf, *ot = obuf + BUFSIZE - 1;
    
    char read_char() {
      if (is == it)
        it = (is = ibuf) + fread(ibuf, 1, BUFSIZE, stdin);
      return *is++;
    }
    
    int read_int() {
      int x = 0, f = 1;
      char c = read_char();
      while (!isdigit(c)) {
        if (c == '-') f = -1;
        c = read_char();
      }
      while (isdigit(c)) x = x * 10 + c - '0', c = read_char();
      return x * f;
    }
    
    ll read_ll() {
      ll x = 0, f = 1;
      char c = read_char();
      while (!isdigit(c)) {
        if (c == '-') f = -1;
        c = read_char();
      }
      while (isdigit(c)) x = x * 10 + c - '0', c = read_char();
      return x * f;
    }
    
    void read_string(char* s) {
      char c = read_char();
      while (isspace(c)) c = read_char();
      while (!isspace(c)) *s++ = c, c = read_char();
      *s = 0;
    }
    
    void flush() {
      fwrite(obuf, 1, os - obuf, stdout);
      os = obuf;
    }
    
    void print_char(char c) {
      *os++ = c;
      if (os == ot) flush();
    }
    
    void print_int(int x) {
      static char q[20];
      if (!x) print_char('0');
      else {
        if (x < 0) print_char('-'), x = -x;
        int top = 0;
        while (x) q[top++] = x % 10 + '0', x /= 10;
        while (top--) print_char(q[top]);
      }
    }
    
    void print_ll(ll x) {
      static char q[20];
      if (!x) print_char('0');
      else {
        if (x < 0) print_char('-'), x = -x;
        int top = 0;
        while (x) q[top++] = x % 10 + '0', x /= 10;
        while (top--) print_char(q[top]);
      }
    }
    
    struct flusher_t {
      ~flusher_t() {
        flush();
      }
    } flusher;
    
    };
    using namespace io;
    
    const int Mod = 998244353;
    const int N = 3e5 + 10;
    const int LOG = 50;
    
    int rt[N], ls[N * LOG], rs[N * LOG], val[N * LOG], tag[N * LOG];
    int ld[N], rd[N], tot = 0;
    int n, m, w[N], pre[N], pos[N];
    
    int add(int a, int b) {
      return (a += b) >= Mod ? a - Mod : a;
    }
    
    int sub(int a, int b) {
      return (a -= b) < 0 ? a + Mod : a; 
    }
    
    int mul(int a, int b) {
      return (long long) a * b % Mod;
    } 
    
    int fast_pow(int a, int b) {
      int res = 1;
      for (; b; b >>= 1, a = mul(a, a))
        if (b & 1) res = mul(res, a);
      return res;
    }
    
    void pushup(int t) {
      val[t] = add(val[ls[t]], val[rs[t]]);
    }
    
    void pushnow(int t, int vl) {
      if (!t) return;
      val[t] = mul(val[t], vl);
      tag[t] = mul(tag[t], vl);
    }
    
    void pushdown(int t) {
      if (tag[t] != 1) {
        pushnow(ls[t], tag[t]);
        pushnow(rs[t], tag[t]);
        tag[t] = 1;
      }
    }
      
    void insert(int &t, int l, int r, int pos) {
      t = ++tot;
      tag[t] = val[t] = 1;
      if (l == r) return;
      int mid = (l + r) >> 1;
      if (pos <= mid) insert(ls[t], l, mid, pos);
      else insert(rs[t], mid + 1, r, pos);
    }
      
    int merge(int x, int y, int sumx, int sumy, int promin, int promax) {
      if (!y) {pushnow(x, sumx); return x;}
      if (!x) {pushnow(y, sumy); return y;}
      pushdown(x), pushdown(y);
      int prex = val[ls[x]], sufx = val[rs[x]];
      int prey = val[ls[y]], sufy = val[rs[y]];
      // 必须要提前声明 不然会被修改 
      ls[x] = merge(ls[x], ls[y], add(sumx, mul(promin, sufy)), add(sumy, mul(promin, sufx)), promin, promax);
      rs[x] = merge(rs[x], rs[y], add(sumx, mul(promax, prey)), add(sumy, mul(promax, prex)), promin, promax);
      pushup(x);
      return x;
    }
      
    void dfs(int u) {
      if (!u) return;
      dfs(ld[u]), dfs(rd[u]);
      if (!ld[u]) {
        insert(rt[u], 1, m, pos[u]);
      } else if (!rd[u]) { // 特判
        rt[u] = rt[ld[u]]; 
      } else {
        rt[u] = merge(rt[ld[u]], rt[rd[u]], 0, 0, sub(1, w[u]), w[u]); 
      }
    }
      
    int calc(int t, int l, int r) {
      if (!t) return 0;
      if (l == r) return mul(mul(mul(val[t], val[t]), pre[l]), l);
      pushdown(t);
      int mid = (l + r) >> 1;
      return add(calc(ls[t], l, mid), calc(rs[t], mid + 1, r));
    }
      
    int main() {
    #ifdef dream_maker
      freopen("input.txt", "r", stdin);
    #endif
      n = read_int(); 
      int inv = fast_pow(10000, Mod - 2);
      for (int i = 1; i <= n; i++) {
        int fa = read_int();
        if (!ld[fa]) ld[fa] = i;
        else rd[fa] = i;
      }
      for (int i = 1; i <= n; i++) { 
        w[i] = read_int();
        if (ld[i]) w[i] = mul(w[i], inv);
        else pre[++m] = w[i];
      }
      sort(pre + 1, pre + m + 1);
      for (int i = 1; i <= n; i++)
        if (!ld[i]) pos[i] = lower_bound(pre + 1, pre + m + 1, w[i]) - pre;
      dfs(1);
      print_int(calc(rt[1], 1, m));
      return 0;
    }
    
  • 相关阅读:
    PowerTalk第一个版本儿控件
    PowerTalk第二个版本,支持(Msn回复信息)
    自然语言处理著作或期刊名称
    自然语言处理(NLP)网上资源整理 (转)
    TFIDF
    计算机科学及工程
    自然语言处理与计算语言学书籍汇总之一:国外书籍
    UVa 10696 f91
    缓存
    操作必须使用一个可更新的查询(转)
  • 原文地址:https://www.cnblogs.com/dream-maker-yk/p/10111134.html
Copyright © 2011-2022 走看看