题解:P12518 「MSTOI-R1」Easy question
P12518 「MSTOI-R1」Easy question题解
一道非常好的学习前缀和和利用数学优化答案的题。
碍于篇幅我不详细说明前缀和,不会的同学可以补习一下。我默认你们都会前缀和。
思路
这道题没有任何的区间修改操作。不难发现,询问一二三都是围绕区间求和展开的,所以可以大胆使用前缀和。你也可以使用线段树,但线段树可能有些大炮打蚊子了。
因为询问二三中都出现了幂运算且指数 \(1 \le k \le 20\),所以我们可以创建一个前缀和二维数组 \(powsum_{i,j}\) 用来预处理不同次方的前缀和,其中 \(1 \le i \le 21\),\(1 \le j \le 1000000\)。数组 \(powsum\) 中 \(i\),\(j\) 表示在 \(i\) 次方下 \(1 \sim j\) 的前缀和。变量 \(l\),\(r\) 表示询问的左右区间。
在输入时可以输入 \(powsum_{1,i}\),表示第 \(i\) 个数字的 \(1\) 次方(就是原数)。再预处理第 \(i\) 个数字的 \(j\) 次方,其中 \(2 \le j \le 20\)。输入完成后分别对不同次方做前缀和。这样就可以在询问时,快速求出答案了。
对于询问一,直接输出 powsum[1][r]-powsum[1][l-1]。对于询问二 \(\sum\limits_{i=l}^{r}{a_i}^k\) 可以直接输出 powsum[k][r]-powsum[k][l-1]。
对于询问三则有些麻烦。对于 \((r-l+1)\cdot \sum\limits_{i=l}^r\left(a_i-\overline a\right)^2\),变量 \(len\) 表示 \(r - l+1\),变量 \(sum\) 表示 \(l \sim r\) 的区间和,变量 \(sum\_sq\) 表示二次方下 \(l \sim r\) 的区间和。就可以得到平均数 \(\overline a\) 为 \(sum \div len\)。根据完全平方式得 \((a_i-\overline a)^2 = (a_i)^2 + (\overline a)^2-2 \times a_i \times \overline a\)。可得 \(len \times \sum(a_i)^2\) 为 \(sum\_sq \times len\),\(len \times (\overline a)^2\) 为 \((sum \div len)^2 \times len \to sum^2 \div len\),\(2 \times \sum a_i \times \overline a \times len\) 为 \(2\times sum \times len \times sum \div len \to 2 \times sum^2\)。
询问三代码如下:
l=read(),r=read();
ll sum = (powsum[1][r] - powsum[1][l - 1] + mod) % mod;
ll sum_sq = (powsum[2][r] - powsum[2][l - 1] + mod) % mod;
ll part1 = ((ll)(r - l + 1) * sum_sq) % mod;
ll part2 = (sum * sum) % mod;
ll ans = (part1 - part2 + mod) % mod;
write(ans);
理解不了的同学可以去看看方差那道题,题解第一个有一个详细的公式推导。
另一些优化和注意事项
注意使用较快的读入输出方式,注意模数和数组大小。
CODE
如果有讲述不清或细微差异请以代码为主自行理解。还是懒得加注释。
#include <bits/stdc++.h>
using namespace std;
#define ll long long
ll mod = 998244353;
ll powsum[21][1001000];
ll read() {
ll x = 0, f = 1;
char ch = getchar();
while (ch < '0' || ch > '9') {
if (ch == '-') f = -1;
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = x * 10 + (ch ^ 48);
ch = getchar();
}
return x * f;
}
void write(ll x) {
if (x < 0) {
putchar('-');
x = -x;
}
if (x > 9) write(x / 10);
putchar(x % 10 + '0');
}
int main() {
int n, q, op, l, r, k;
n=read(),q=read();
for (int i = 1; i <= n; i++) {
powsum[1][i]=read();
for(int j=2;j<=20;j++){
powsum[j][i]=powsum[j-1][i]*powsum[1][i]%mod;
}
}
for(int i=1;i<=20;i++){
for(int j=1;j<=n;j++){
powsum[i][j]+=powsum[i][j-1];
powsum[i][j]%=mod;
}
}
for (int i = 1; i <= q; i++) {
op=read();
if (op == 1) {
l=read(),r=read();
write((powsum[1][r] - powsum[1][l - 1] + mod) % mod);
} else if (op == 2) {
l=read(),r=read(),k=read();
write((powsum[k][r] - powsum[k][l - 1] + mod) % mod);
} else {
l=read(),r=read();
ll sum = (powsum[1][r] - powsum[1][l - 1] + mod) % mod;
ll sum_sq = (powsum[2][r] - powsum[2][l - 1] + mod) % mod;
ll part1 = ((ll)(r - l + 1) * sum_sq) % mod;
ll part2 = (sum * sum) % mod;
ll ans = (part1 - part2 + mod) % mod;
write(ans);
}
putchar('\n');
}
return 0;
}

浙公网安备 33010602011771号