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\)
思路
推柿子
那么现在,\(\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;
}

浙公网安备 33010602011771号