题解: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;
}

posted @ 2025-07-26 14:36  WMWD  阅读(3)  评论(0)    收藏  举报