F - Random Update Query

发布时间 2023-12-12 14:51:33作者: onlyblues

F - Random Update Query

Problem Statement

You are given an integer sequence $A = (A_1, A_2, \ldots, A_N)$ of length $N$.

We will perform the following operation on $A$ for $i = 1, 2, \ldots, M$ in this order.

  • First, choose an integer between $L_i$ and $R_i$, inclusive, uniformly at random and denote it as $p$.
  • Then, change the value of $A_p$ to the integer $X_i$.

For the final sequence $A$ after the above procedure, print the expected value, modulo $998244353$, of $A_i$ for each $i = 1, 2, \ldots, N$.

How to print expected values modulo $998244353$

It can be proved that the expected values sought in this problem are always rational. Furthermore, the constraints of this problem guarantee that if each of those expected values is expressed as an irreducible fraction $\frac{y}{x}$, then $x$ is not divisible by $998244353$.

Now, there is a unique integer $z$ between $0$ and $998244352$, inclusive, such that $xz \equiv y \pmod{998244353}$. Report this $z$.

Constraints

  • All input values are integers.
  • $1 \leq N, M \leq 2 \times 10^5$
  • $0 \leq A_i \leq 10^9$
  • $1 \leq L_i \leq R_i \leq N$
  • $0 \leq X_i \leq 10^9$

Input

The input is given from Standard Input in the following format:

$N$ $M$
$A_1$ $A_2$ $\ldots$ $A_N$
$L_1$ $R_1$ $X_1$
$L_2$ $R_2$ $X_2$
$\vdots$
$L_M$ $R_M$ $X_M$

Output

Print the expected values $E_i$ of the final $A_i$ for $i = 1, 2, \ldots, N$ in the format below, separated by spaces.

$E_1$ $E_2$ $\ldots$ $E_N$

Sample Input 1

5 2
3 1 4 1 5
1 2 2
2 4 0

Sample Output 1

499122179 1 665496238 665496236 5

We start from the initial state $A = (3, 1, 4, 1, 5)$ and perform the following two operations.

  • The first operation chooses $A_1$ or $A_2$ uniformly at random, and changes its value to $2$.
  • Then, the second operation chooses one of $A_2, A_3, A_4$ uniformly at random, and changes its value to $0$.

As a result, the expected values of the elements in the final $A$ are $(E_1, E_2, E_3, E_4, E_5) = (\frac{5}{2}, 1, \frac{8}{3}, \frac{2}{3}, 5)$.


Sample Input 2

2 4
1 2
1 1 3
2 2 4
1 1 5
2 2 6

Sample Output 2

5 6

Sample Input 3

20 20
998769066 273215338 827984962 78974225 994243956 791478211 891861897 680427073 993663022 219733184 570206440 43712322 66791680 164318676 209536492 137458233 289158777 461179891 612373851 330908158
12 18 769877494
9 13 689822685
6 13 180913148
2 16 525285434
2 14 98115570
14 17 622616620
8 12 476462455
13 17 872412050
14 15 564176146
7 13 143650548
2 5 180435257
4 10 82903366
1 2 643996562
8 10 262860196
10 14 624081934
11 13 581257775
9 19 381806138
3 12 427930466
6 19 18249485
14 19 682428942

Sample Output 3

821382814 987210378 819486592 142238362 447960587 678128197 687469071 405316549 318941070 457450677 426617745 712263899 939619994 228431878 307695685 196179692 241456697 12668393 685902422 330908158

 

解题思路

  令 $E_i$ 表示第 $i$ 个数的期望值,在没有执行任何操作前显然都有 $E_i = a_i$。对于某个询问 $(l,r,x)$ 如果 $i \in [l,r]$,记 $\lambda = r-l+1$,那么第 $i$ 个数有 $\frac{1}{\lambda}$ 的概率变成 $x$,有 $\frac{\lambda - 1}{\lambda}$ 的概率还是其本身即 $E_i$,因此第 $i$ 个数的期望就变成了 $E_i \gets \frac{1}{\lambda} \cdot x + \frac{\lambda - 1}{\lambda} \cdot E_i$。

  显然对于每个询问我们不可能一个个数去修改,这样做的时间复杂度为 $O(nm)$。可以发现在每个询问中我们都需要对区间 $[l,r]$ 中的每个 $E_i$ 先乘上一个数 $\frac{\lambda - 1}{\lambda}$,再加上一个数 $\frac{x}{\lambda}$,因此容易想到用线段树去维护。

  线段树的节点维护两个懒标记,$\text{sum}$ 表示这个区间累加的值,$\text{prod}$ 表示这个区间累乘的值。在一次操作中假设要对某个区间的数先乘上 $p$ 再加上 $s$,考虑懒标记如何更新。不妨假设当前的懒标记最后作用在 $E_i$ 上,那么 $E_i$ 就会被更新成 $E_i \times \text{prod} + \text{sum}$,再更新就会得到 $E_i \times (\text{prod} \cdot p) + (\text{sum} \cdot p + s)$,即对应的懒标记的更新就是 $\displaylines{\begin{cases} \text{sum} = \text{sum} \cdot p+ s \\ \text{prod} = \text{prod}\cdot p \end{cases}}$。

  在执行完所有的操作后只需单点查询,那么第 $i$ 个数的期望就是 $a_i \times \text{prod} + \text{sum}$。可以发现线段树只有下传懒标记的操作,而没有向上更新的操作。

  另外这题还有对应的扩展,参考题目 [AHOI2009] 维护序列,需要查询区间和,每次的修改操作是对某个区间加上或乘上某个数。

  AC 代码如下,时间复杂度为 $O(m \log{n})$:

#include <bits/stdc++.h>
using namespace std;

const int N = 2e5 + 10, mod = 998244353;

int a[N];
struct Node {
    int l, r, s, p;
}tr[N * 4];

void build(int u, int l, int r) {
    tr[u] = {l, r, 0, 1};
    if (l == r) {
        tr[u].p = a[l] % mod;
    }
    else {
        int mid = l + r >> 1;
        build(u << 1, l, mid);
        build(u << 1 | 1, mid + 1, r);
    }
}

void pushdown(int u) {
    if (tr[u].s || tr[u].p != 1) {
        tr[u << 1].s = (1ll * tr[u << 1].s * tr[u].p + tr[u].s) % mod;
        tr[u << 1].p = 1ll * tr[u << 1].p * tr[u].p % mod;
        tr[u << 1 | 1].s = (1ll * tr[u << 1 | 1].s * tr[u].p + tr[u].s) % mod;
        tr[u << 1 | 1].p = 1ll * tr[u << 1 | 1].p * tr[u].p % mod;
        tr[u].s = 0;
        tr[u].p = 1;
    }
}

void modify(int u, int l, int r, int s, int p) {
    if (tr[u].l >= l && tr[u].r <= r) {
        tr[u].s = (1ll * tr[u].s * p + s) % mod;
        tr[u].p = 1ll * tr[u].p * p % mod;
    }
    else {
        pushdown(u);
        int mid = tr[u].l + tr[u].r >> 1;
        if (l <= mid) modify(u << 1, l, r, s, p);
        if (r >= mid + 1) modify(u << 1 | 1, l, r, s, p);
    }
}

int query(int u, int x) {
    if (tr[u].l == tr[u].r) return (tr[u].s + tr[u].p) % mod;
    pushdown(u);
    if (x <= tr[u].l + tr[u].r >> 1) return query(u << 1, x);
    return query(u << 1 | 1, x);
}

int qmi(int a, int k) {
    int ret = 1;
    while (k) {
        if (k & 1) ret = 1ll * ret * a % mod;
        a = 1ll * a * a % mod;
        k >>= 1;
    }
    return ret;
}

int main() {
    int n, m;
    scanf("%d %d", &n, &m);
    for (int i = 1; i <= n; i++) {
        scanf("%d", a + i);
    }
    build(1, 1, n);
    while (m--) {
        int l, r, x;
        scanf("%d %d %d", &l, &r, &x);
        int t = r - l + 1, p = qmi(t, mod - 2);
        modify(1, l, r, 1ll * x * p % mod, (t - 1ll) * p % mod);
    }
    for (int i = 1; i <= n; i++) {
        printf("%d ", query(1, i));
    }
    
    return 0;
}

 

参考资料

  Editorial - AtCoder Beginner Contest 332:https://atcoder.jp/contests/abc332/editorial/7926