LOJ2537. 「PKUWC2018」Minimax【機率DP+線段樹合併】

LINKc++


思路

首先暴力\(n^2\)是很好想的,就是把當前節點機率按照權值大小作前綴和和後綴和而後對於每個值直接在另外一個子樹裏面算出貢獻和就能夠了,注意乘上選最大的機率是小於當前權值的部分,選最小是大於當前權值的部分git

而後考慮怎麼優化優化

用線段樹合併來作spa

每次向左遞歸的時候就把x右子樹對y左子樹的貢獻加上,把y右子樹對x左子樹的貢獻加上code

每次向左遞歸的時候就把x左子樹對y右子樹的貢獻加上,把y左子樹對x右子樹的貢獻加上遞歸

考慮每一個節點,左邊的區間貢獻必定會被統計徹底,右邊的區間貢獻必定會被統計徹底get

而後這樣統計下來全部的點都是被更新了的input

因此只須要動態開點線段樹維護一個區間和和區間乘標記就能夠了string

而後就作完了it


注意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;
}
相關文章
相關標籤/搜索