F - Two Sequence Queries

F - Two Sequence Queries

Problem Statement

You are given sequences of length N, A=(A1,A2,,AN) and B=(B1,B2,,BN).
You are also given Q queries to process in order.

There are three types of queries:

  • 1 l r x : Add x to each of Al,Al+1,,Ar.
  • 2 l r x : Add x to each of Bl,Bl+1,,Br.
  • 3 l r : Print the remainder of i=lr(Ai×Bi) when divided by 998244353.

Constraints

  • 1N,Q2×105
  • 0Ai,Bi109
  • 1lrN
  • 1x109
  • All input values are integers.
  • There is at least one query of the third type.

Input

The input is given from Standard Input in the following format. Here, queryi (1iQ) is the i-th query to be processed.

N Q
A1 A2 AN
B1 B2 BN
query1
query2

queryQ

Each query is given in one of the following formats:

1 l r x

2 l r x

3 l r

Output

If there are K queries of the third type, print K lines.
The i-th line (1iK) should contain the output for the i-th query of the third type.


Sample Input 1

5 6
1 3 5 6 8
3 1 2 1 2
3 1 3
1 2 5 3
3 1 3
1 1 3 1
2 5 5 2
3 1 5

Sample Output 1

16
25
84

Initially, A=(1,3,5,6,8) and B=(3,1,2,1,2). The queries are processed in the following order:

  • For the first query, print (1×3)+(3×1)+(5×2)=16 modulo 998244353, which is 16.
  • For the second query, add 3 to A2,A3,A4,A5. Now A=(1,6,8,9,11).
  • For the third query, print (1×3)+(6×1)+(8×2)=25 modulo 998244353, which is 25.
  • For the fourth query, add 1 to A1,A2,A3. Now A=(2,7,9,9,11).
  • For the fifth query, add 2 to B5. Now B=(3,1,2,1,4).
  • For the sixth query, print (2×3)+(7×1)+(9×2)+(9×1)+(11×4)=84 modulo 998244353, which is 84.

Thus, the first, second, and third lines should contain 16, 25, and 84, respectively.


Sample Input 2

2 3
1000000000 1000000000
1000000000 1000000000
3 1 1
1 2 2 1000000000
3 1 2

Sample Output 2

716070898
151723988

Make sure to print the sum modulo 998244353 for the third type of query.

 

解题思路

  将修改操作统一成同时对区间内的 aibi 进行修改。具体的,对于操作 1,对 i[l,r] 内的每个 ai 加上 x,每个 bi 加上 0;对于操作 2,则每个 ai 加上 0,每个 bi 加上 x。由于涉及到区间加,显然要用到线段树,下面考虑需要维护哪些信息。

  当对区间 [l,r] 内的 ai 加上 xbi 加上 y,原先的 i=lraibi 就会变成

i=lr(ai+x)(bi+y)=i=lraibi+aiy+bix+xy=i=lraibi+yi=lrai+xi=lrbi+xy(rl+1)

  因此对于每个维护着区间 [l,r] 的线段树节点,我们需要维护 s1 表示区间内 aibi 的和;s2 表示区间 ai 的和;s3 表示区间 bi 的和。当对整个节点的 ai 加上 xbi 加上 y,那么对应的更新操作就是 {s1s1+s2y+s3x+xy(rl+1)s2s2+x(rl+1)s3s3+y(rl+1)

  AC 代码如下,时间复杂度为 O(qlogn)

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

typedef long long LL;

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

int a[N], b[N];
struct Node {
    int l, r, s1, s2, s3, sum1, sum2;
}tr[N * 4];

void pushup(int u) {
    tr[u].s1 = (tr[u << 1].s1 + tr[u << 1 | 1].s1) % mod;
    tr[u].s2 = (tr[u << 1].s2 + tr[u << 1 | 1].s2) % mod;
    tr[u].s3 = (tr[u << 1].s3 + tr[u << 1 | 1].s3) % mod;
}

void build(int u, int l, int r) {
    tr[u] = {l, r};
    if (l == r) {
        tr[u].s1 = 1ll * a[l] * b[l] % mod;
        tr[u].s2 = a[l] % mod;
        tr[u].s3 = b[l] % mod;
    }
    else {
        int mid = l + r >> 1;
        build(u << 1, l, mid);
        build(u << 1 | 1, mid + 1, r);
        pushup(u);
    }
}

void upd(int u, int x, int y) {
    int len = tr[u].r - tr[u].l + 1;
    tr[u].s1 = (tr[u].s1 + 1ll * tr[u].s2 * y + 1ll * tr[u].s3 * x + 1ll * x * y % mod * len) % mod;
    tr[u].s2 = (tr[u].s2 + 1ll * x * len) % mod;
    tr[u].s3 = (tr[u].s3 + 1ll * y * len) % mod;
    tr[u].sum1 = (tr[u].sum1 + x) % mod;
    tr[u].sum2 = (tr[u].sum2 + y) % mod;
}

void pushdown(int u) {
    upd(u << 1, tr[u].sum1, tr[u].sum2);
    upd(u << 1 | 1, tr[u].sum1, tr[u].sum2);
    tr[u].sum1 = tr[u].sum2 = 0;
}

void modify(int u, int l, int r, int x, int y) {
    if (tr[u].l >= l && tr[u].r <= r) {
        upd(u, x, y);
    }
    else {
        pushdown(u);
        int mid = tr[u].l + tr[u].r >> 1;
        if (l <= mid) modify(u << 1, l, r, x, y);
        if (r >= mid + 1) modify(u << 1 | 1, l, r, x, y);
        pushup(u);
    }
}

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

int main() {
    int n, m;
    scanf("%d %d", &n, &m);
    for (int i = 1; i <= n; i++) {
        scanf("%d", a + i);
    }
    for (int i = 1; i <= n; i++) {
        scanf("%d", b + i);
    }
    build(1, 1, n);
    while (m--) {
        int op, l, r, x;
        scanf("%d %d %d", &op, &l, &r);
        if (op == 1) {
            scanf("%d", &x);
            modify(1, l, r, x, 0);
        }
        else if (op == 2) {
            scanf("%d", &x);
            modify(1, l, r, 0, x);
        }
        else {
            printf("%d\n", query(1, l, r));
        }
    }
    
    return 0;
}

 

参考资料

  Editorial - SuntoryProgrammingContest2024(AtCoder Beginner Contest 357):https://atcoder.jp/contests/abc357/editorial/10187

posted @   onlyblues  阅读(72)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
历史上的今天:
2022-06-09 闪烁
Web Analytics
点击右上角即可分享
微信分享提示