AT_abc452_e You WILL Like Sigma Problem 题解

前言

这道题赛时推错了三次,赛后30分钟搞定,还是得多练练拆\(\sum\)

看到题目名称第一的想法就是 I WILL NEVER Like Sigma Problem

题面

给定长度为\(n\)的数组\(A\)和长度为\(m\)的数组\(B\),求$\displaystyle{(\sum_{i=1}^{n} \sum_{j=1}^m A_i \times B_j \times (i \mod j)) \mod 998244353} $

\(1 \leq n,m,A_i,B_j \leq 5 \times 10^5\)

思路

推柿子

\[\sum_{i=1}^{n} \sum_{j=1}^m( A_i \times B_j \times (i \mod j)) \]

\[= \sum_{j=1}^{m} \sum_{i=1}^n (A_i \times B_j \times (i \mod j)) \]

\[= \sum_{j=1}^{m} \sum_{i=1}^n (A_i \times B_j \times (i - \left \lfloor \frac{i}{j} \right \rfloor \times j)) \]

\[= \sum_{j=1}^{m} \sum_{i=1}^n ( A_i \times B_j \times i - A_i \times B_j \times \left \lfloor \frac{i}{j} \right \rfloor \times j) \]

\[= \sum_{j=1}^{m} (B_j \times \sum_{i=1}^n (i \times A_i)) - \sum_{j=1}^m (j \times B_j \times \sum_{i=1}^{n}(A_i \times \left \lfloor \frac{i}{j} \right \rfloor )) \]

\[= \sum_{j=1}^{m} B_j \times \sum_{i=1}^n (i \times A_i) - \sum_{j=1}^m (j \times B_j \times \sum_{i=1}^{n}(A_i \times \left \lfloor \frac{i}{j} \right \rfloor )) \]

那么现在,\(\displaystyle{\sum_{j=1}^{m} B_j \times \sum_{i=1}^n (i \times A_i)}\)是可以直接\(O(n)\)计算的
\(\displaystyle{\sum_{j=1}^m (j \times B_j \times \sum_{i=1}^{n}(A_i \times \left \lfloor \frac{i}{j} \right \rfloor ))}\)部分中,我们枚举\(j\),那么可以很容易的计算出\(j \times B_j\),而\(\displaystyle{\sum_{i=1}^{n}(A_i \times \left \lfloor \frac{i}{j} \right \rfloor)}\),我们可以用\(O(n/j)\)的方法,把\(n\)分成\((m-1,2m-1], (2m-1,3m-1] ,...,(km-1,n]\)\(\left \lceil \frac{n}{j} \right \rceil\)个区间,那么每个区间的\(\left \lfloor \frac{x}{j} \right \rfloor (x \in \text{这个区间})\)都相等,那么就可以用前缀和\(O(1)\)求出这个区间的\(A_i\)之和然后在乘上区间的\(\left \lfloor \frac{x}{j} \right \rfloor\)得到\(\displaystyle{\sum_{i}^{这个区间}(A_i \times \left \lfloor \frac{i}{j} \right \rfloor)}\)最后将所有的区间的答案累加即可。

代码:

#include <bits/stdc++.h>
#define int long long
using namespace std;
int a[500001];
int b[500001];
int ss[500001]; // a的前缀和
const int mod = 998244353;
signed main()
{
    int n, m;
    cin >> n >> m;
    int sb = 0, ssa = 0; // sb: B的所有数字之和, ssa: 所有Ai * i之和
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
        ss[i] = ss[i - 1] + a[i];  // 求前缀和
        ss[i] %= mod;
        ssa += i * a[i] % mod; // 累加
        ssa %= mod;
    }
    for (int j = 1; j <= m; j++)
    {
        cin >> b[j];
        sb += b[j]; // 累加
        sb %= mod;
    }
    int ans = 0;
    ans = ssa * sb % mod; // 第一部分
    for (int j = 1; j <= m; j++)
    {
        int s = 0, p = 0, c = 0; // s:计算Ai * |_i / j_|之和, p:上一次的右端,也就是这一次的左端(左开)c:当前除以j的商
        for (int k = j - 1; ; k += j, c++) // 每个区间的右端
        {
            s += c * (ss[min(n, k)] - ss[p]) % mod; //p就是左端,最开始的时候c = 0,所以p的初始值不太影响,注意右界最多为n
            s %= mod;
            if (k > n) break; // 没有下一个区间了
            p = k; // 记下右端,也就是下一个区间的左端(左开)
        }
        ans -= j * b[j] % mod * s % mod; // 乘上j和Bj,减去几个数字连加等于分别减去
        ans %= mod; 
    }
    cout << (ans + mod) % mod << endl; // 记得负数转正
    return 0;
}

祝你\(AC\)~~~~

posted @ 2026-04-05 21:52  MichaelZeng  阅读(2)  评论(0)    收藏  举报