洛谷 P12518 「MSTOI-R1」Easy question 题解

传送门:P12518 「MSTOI-R1」Easy question

写在之前

(机房大佬:我第一眼以为有修改操作,就想到建20棵树。)实际上不用建20棵树,学算法学多了导致的。

题意大意

给一个序列 \(a\),长度为 \(n\),你需要进行下面三种操作:

  1. 1 l r 表示求 \(\sum\limits_{i=l}^{r}a_i\)

  2. 2 l r k 表示求 \(\sum\limits_{i=l}^{r}{a_i}^k\)

  3. 3 l r 表示求 \((r-l+1)\cdot \sum\limits_{i=l}^r\left(a_i-\overline a\right)^2\),其中 \(\overline a\) 为序列中 \([l,r]\) 的平均数。

思路分析

操作1

发现就是求序列区间 \([l,r]\) 的和,于是很容易想到前缀和做法,前缀和用数组 \(sum\) 存储。

操作2

求序列区间 \([l,r]\) 中每个数的 \(k\) 次方的和。一通计算下来发现没有时间复杂度合理的简便计算。然后想到在输入原序列时对每个数的 \(k\) 次方进行预处理,同样可以进行前缀和操作,于是将前缀和数组开成二维数组,其中 \(sum_{i,j}\) 表示序列前 \(i\) 个数的 \(j\) 次方的和。

操作3

一个形似求方差的操作,我们可以将这个式子先写出来:设 \(ans = (r-l+1)\cdot \sum\limits_{i=l}^r\left(a_i-\overline a\right)^2\)

则可以推出如下式子。

\(\begin{aligned} ans &= (r - l + 1) \cdot [(a_l - \overline a)^2 + (a_{l+1} - \overline a)^2 + \cdots + (a_r -\overline a)^2] \\ &= (r - l + 1) \cdot [(a_l^2 - 2a_l\overline a + {\overline a}^2) + (a_{l+1}^2 - 2a_{l+1}\overline a + {\overline a}^2) + \cdots + (a_r^2 - 2a_r\overline a + \overline a^2)] \\ &=(r - l + 1) \cdot [\sum\limits_{i=l}^{r}{a_i}^2 - 2\overline a \cdot \sum\limits_{i=l}^{r}{a_i} + (r - l + 1) \cdot \overline a^2] \end{aligned}\)

而根据 \(\overline a = \frac{\sum\limits_{i=l}^{r}{a_i}}{r-l+1}\),将 \((r - l + 1)\) 乘入括号,得到式子:\(ans = (r - l + 1)\cdot \sum\limits_{i=l}^{r}{a_i} ^2 - 2(\sum\limits_{i=l}^{r}{a_i})^2 + (\sum\limits_{i=l}^{r}{a_i})^2\)

最后化简可得\(ans = (r - l + 1)\cdot \sum\limits_{i=l}^{r}{a_i} ^2 - (\sum\limits_{i=l}^{r}{a_i})^2\)

化简后可知答案一定是一个整数。

而我们已经用了前缀和数组存储了序列前 \(i\) 个数的 \(2\) 次方的和,因此操作3的时间复杂度为 \(O(1)\)

代码

long long n, q;
unsigned long long opt, l, r, k, ans, a;
unsigned long long sum_1, sum_2;
unsigned long long mod = 998244353;
unsigned long long num[1000010];//原数组
unsigned long long sum[1000010][21];//前缀和数组,sum[i][j] 表示序列前i个数的j次方的和 
int main(){
	ios::sync_with_stdio(false);//解绑操作,加快数据输入输出速度 
	cin.tie(0);
	cout.tie(0);
	
	cin >> n >> q;
	for(int i = 1; i <= n; i++){
		cin >> num[i];
		a = num[i];
		for(int j = 1; j <= 20; j++){//前缀和数组预处理 
			sum[i][j] =(sum[i - 1][j] + a + mod) % mod;
			a = a * num[i] % mod;
		}
	}
	
	for(int i = 1; i <= q; i++){
		cin >> opt;
		if(opt == 1){//操作1 
			cin >> l >> r;
			ans = sum[r][1] - sum[l - 1][1];
		} else if(opt == 2){//操作2 
			cin >> l >> r >> k;
			ans = sum[r][k] - sum[l - 1][k];
		}else{//操作3 
			cin >> l >> r;
			sum_1 = sum[r][1] - sum[l - 1][1];//区间和 
			sum_2 = sum[r][2] - sum[l - 1][2];//区间平方和 
			ans = ((sum_2 + mod) % mod * (r - l + 1 + mod) % mod - (sum_1 * sum_1) % mod + mod) % mod;
		}
		ans += mod;
		cout << ans % mod << '\n';
	}
	return 0;
} 

写在最后

赛时被模数嗯控,最后40分遗憾离场,本蒟蒻要重修语法了(悲)。

帮到你的话请点赞题解喵,点赞题解谢谢喵

posted @ 2025-07-21 20:59  牢毛暂时好耶  阅读(5)  评论(1)    收藏  举报