前缀和

前言

  • 鸣谢学长Kersen
  • 这两天推式子推的我挺快乐的的就,记录一下自己推数学式子的过程
  • updata on :2020.12.3
    修改了自己写炸的latex顺便再加一个题CF1422C难受,没有嫖到学长的学习资料还是有点小失落,但貌似我可以跟别人嫖一下

前缀和聚集地

定义一个函数
\(s(l,r) = \sum\limits_{i = l}^{r}a_i\times \sum\limits_{i = l}^{r}b_i\)
要求

\(\sum\limits_{l = 1}^{n}\sum\limits_{r = l}^{n}s(l,r)\)

1. 可以凑到一起,得到这样一个式子

\[\sum\limits_{l = 1}^{n}\sum\limits_{r = 1}^{n}(\sum\limits_{i = l}^{r}a_i\times \sum\limits_{i = l}^{r}b_i) \]

2. 进一步讲里面的化简

\(suma[]\)\(a\)的前缀和, \(sumb[]\)\(b\)的前缀和
将最后一个\(\sum\limits_{i = l}^{r} b_i\)变形为\(sumb[r]-sumb[l-1]\)

3.得到一个(消去一重循环)

\[\sum\limits_{l = 1}^{n}\sum\limits_{r = 1}^{n}[\sum\limits_{i = l}^{r}a_i\times(sumb[r]-sumb[l-1])] \]

\(sumb[]\)数组可以通过输入的时候预处理来解决,考虑再消去一重循环,发现\(\sum\limits_{i = l}^{r}a_i\)这里也可以预处理出来,就

4.进而得到了下面的式子

\[\sum \limits_ { l = 1}^{n} \sum\limits_{r = l}^{n} (suma[r] - suma[l-1])\times(sumb[r] - sumb[l-1]) \]

发现现在的时间复杂度已经由\(n^4\) 降到了\(n^2\),但是仍然不够优秀,再次考虑再去消去一重循环将括号内的式子进行处理

\[\sum \limits_ { l = 1}^{n} \sum\limits_{r = l}^{n}(suma[r]\times sumb[r] + suma[l-1]\times suma[l-1] - suma[r] \times sumb[l - 1] - sumb[r] \times suma[l - 1] - \]

  • \(qz[i]\)\(suma[i]\times sumb[i]\)的前缀和
  • \(qza[i]\)\(suma[i]\)的前缀和
  • \(qzb[i]\)\(sumb[i]\)的前缀和

5.可以将给式子这样套一个括号

\(\sum\limits_{ l = 1}^{n}(\sum\limits_{r = l}^{n}(suma[r]\times sumb[r] + suma[l-1]\times sumb[l-1] - suma[r] \times sumb[l - 1] - sumb[r] \times suma[l - 1])\)

6.式子就变成了

\(\sum \limits_ { l = 1}^{n}\left[\\(qz[n] -qz[l-1]) + \\(n - l +1)\times (suma[l - 1]\times sumb[l-1]) \\- sumb[l-1] \times (qza[n]-qza[l-1])\\ - suma[l-1] \times (qzb[n]-qzb[l-1]) \right]\)

code

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#define int long long 
using namespace std;
const int N = 5e5+100;
const int mod = 1e9+7;
int read(){
	int s = 0 ,f = 1; char ch = getchar();
    while(ch < '0'||ch > '9'){if(ch == '-') f = -1 ; ch = getchar();}
	while(ch >= '0'&&ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();}
	return s * f;
}
int a[N] ,b[N];
int suma[N] ,sumb[N];
int qz[N], qza[N],qzb[N];
signed main(){
	int n = read();
	for(int i = 1 ; i <= n ;i++) {
		a[i] = read();
		suma[i] = (suma[i - 1] + a[i]) % mod;
	}
	for(int i = 1 ; i <= n ;i++ ) {
		b[i] = read();
		sumb[i] = (sumb[i - 1] +  b[i]) % mod;
	}
	for(int i = 1 ; i <= n ;i++) {
		qza[i] = (qza[i - 1] + suma[i]) % mod;
		qzb[i] = (qzb[i-1] + sumb[i]) % mod;
		qz[i] = (qz[i - 1] +  (suma[i] * sumb[i]) % mod) % mod;
		
	}
	int ans = 0;
	for(int i = 1 ; i <= n ;i++) {
		int ans1 = ( (qz[n] - qz[i - 1] + mod) % mod) ;
		int ans2 = ((n - i + 1) * ((suma[i - 1] * sumb[i - 1]) % mod) + mod) % mod ;
		int ans3 = ( suma[i - 1] * ( (qzb[n] - qzb[i - 1] + mod) % mod ) + mod ) % mod; 
		int ans4 = ( sumb[i - 1] * ( (qza[n] - qza[i - 1] + mod) % mod ) + mod )  % mod;
		ans = (ans + ans1 + ans2 - ans3 - ans4 + mod) % mod;
	}
	cout << (ans + mod) % mod;
	
	return 0;
}

不鸽了
因为是学姐的个人题,就不粘链接了
简述题意输入一个\(n\) ,下面一行输入\(n\)个整数,求

\[\sum\limits_{i = 1} ^ {n} \sum\limits_{ j = i+1} ^{n}(a_i-a_j)^2 \]

1. 对于括号里的式子可以很简单的用完全平方公式化简

得到

\[\sum\limits_{i = 1} ^ {n} \sum\limits_{ j = i+1} ^{n}(a_i^2-2a_ia_j+a_j^2) \]

2.仍然还是那个方法套个括号

\[\sum\limits_{i = 1} ^ {n}[\sum\limits_{ j = i+1} ^{n}(a_i^2-2a_ia_j+a_j^2)] \]

  • \(sum_i\)\(a_i\)的前缀和
  • \(pow_i\)\(a_i^2\)的前缀和
  • \(sumpow_i\)\(pow_i\)的前缀和
    化简得到

\[sumpow_n \times(n-1) - \sum\limits_{i = 1}^{n}a_i\times (sum_n - sum_i) \]

就可以得到一个\(O(n)\)的算法,但是我也不知道为啥考场写了个这么奇怪的代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#define int long long
using namespace std;
const  int N = 1e6+100;
const int mod = 998244353 ;

inline int read(){
	int s = 0 ,f = 1; char ch = getchar();
	while(ch < '0'||ch > '9'){if(ch == '-') f = -1 ; ch = getchar();}
	while(ch >= '0'&&ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();}
	return s * f;
}
int w[N],pow[N],sum[N],sum_pow[N];
int ans = 0;
signed main(){
	int n = read() ;
	for(int i = 1 ; i <= n ;i++) {
		w[i] = read();
		sum[i] = (sum[i] + (sum[i-1] + w[i]) % mod) % mod;
		pow[i] = (w[i] % mod * w[i] % mod) % mod;
		sum_pow[i] = (sum_pow[i-1] + pow[i]) % mod;
	}
	ans = ans + (sum_pow[n] * (n - 1) + mod) % mod ;
	for(int i = 1 ; i < n ;i++){
		ans += mod;
		ans = (ans -  2 * w[i] * (sum[n] -sum[i] + mod)) % mod;
	}
	printf("%lld",(ans+ mod) % mod );
	return 0;
} 

学长更改题面,我读错题导致我造了一个题推出另一个题挺有意思的式子,先写在前面
我读为:

读入一个数字串,可以从前面或者后面截取任意长度的一个串求出所有情况,对长度大于1的串,除最后一个数外都乘10,求总和对 1e9+7取模

打个表:
1 2 3 4 5
就会得到这样一个表
\((1) + (10 + 2) +( 10 + 20 + 3 )+ (10 + 20 + 30 + 4)+ (10 + 20 + 30 + 40 + 5)\)
\((2) + (20 + 3) +(20 + 30 + 4 )+ (20 + 30 + 40 + 5)\)
\((3) + (30 + 40) +(30 + 40 + 5)\)
\((4) + (40 + 5)\)
\((5)\)
就会很快乐的得到这样一个式子

  • 数字串长为\(len\)
  • 每一位上的数\(\times\)之后为\(ten[i]\)

\[\sum\limits_{i = 1} ^{len}(num_i \times i + ten_i \times(len-i) \times i) \]

然后样例二过不去,我就知道坏事了,我读错题了,心态当场崩溃,后面的暴力也不想写了,好吧,自己还是考场心态调整的不行
放出自己读错题的代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#define int long long 
using namespace std;
const int N = 2e6+100;
const int mod = 1e9+7;
int read(){
	int s = 0 ,f = 1; char ch = getchar();
	while(ch < '0'||ch > '9'){if(ch == '-') f = -1 ; ch = getchar();}
	while(ch >= '0'&&ch <= '9'){s = s * 10 + ch - '0'; ch = getchar();}
	return s * f;
}
char s[N];
int num[N];
int ten[N];
int sum[N];
int sum_10[N];
signed main(){
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	cin>>s;
	int len = strlen(s);
	for(int i = 0 ; i < len ; i++ ) {
		num[i + 1] = s[i]-'0';
		ten[i + 1] = num[i + 1] * 10 ;
	}
	int ans = 0 ;
	for(int i = 1 ; i <= len ;i++) {
		int ans1 = (num[i] * i) % mod;
		int ans2 = (ten[i] * (len - i) *  i) % mod;
		ans = ( ans + (ans1 + ans2) % mod ) % mod;
	}
	cout << ans % mod;
}

然而真实的题面意思是这样的

  • \(len\)仍然为这个数字串的长度
  • 输入一个数字串,你可以从任意的地方抽取一块连续的部分,剩下的部分可以拼接到一起,然后每个数\(\times 10^{len-i}\) 求出所有情况的的和, 对1e9+7取模

Solution :

答案可以得到这样一个式子
令- \(f(l,r)\)表示字串\([L,R]\)组成的十进制数
考虑枚举删除的子串的最后一位\(x\)得到的十进制数的和为:

\[\begin{aligned} &\sum_{i=1}^{x-1}{\left\{ 10^{n-x}\times f(1,i) +f(x+1,n)\right\}}\\ =& (x-1)\times f(x+1,n) + 10^{n-x}\times \sum_{i=1}^{x-1}{f(1,i)}\\ \end{aligned}\]

好吧我抄的学长博客

//知识点:瞎搞
/*
By:Luckyblock
*/
#include <cctype>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define LL long long
const int kN = 2e6 + 10;
const LL mod = 1e9 + 7;
//=============================================================
int n;
char s[kN];
LL ans, sum, pow10[kN], suf[kN];
//=============================================================
inline int read() {
  int f = 1, w = 0; char ch = getchar();
  for (; !isdigit(ch); ch = getchar()) if (ch == '-') f = -1;
  for (; isdigit(ch); ch = getchar()) w = (w << 3) + (w << 1) + (ch ^ '0');
  return f * w;
}
//=============================================================
int main() {
  scanf("%s", s + 1);
  n = strlen(s + 1);
  pow10[0] = 1;
  for (int i = 1; i <= n; ++ i) {
    pow10[i] = pow10[i - 1] * 10ll % mod;;
  }
  for (int i = n; i >= 1; -- i) {
    suf[i] = suf[i + 1];
    suf[i] += (s[i] - '0') * pow10[n - i] % mod;
    suf[i] %= mod;
  }
  LL val = 0;
  for (int i = 1; i <= n; ++ i) {
    ans += sum * pow10[n - i] % mod + (i - 1) * suf[i] % mod;
    ans %= mod;
    val = (10ll * val % mod + s[i] - '0') % mod;
    sum = (sum + val) % mod;
  }
  printf("%lld\n", ans);
  return 0;
}

我的做法比较……难搞……我自己也不会写这个数学公式……就是前缀 + 后缀维护答案感觉自己是sb

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#define int long long

using namespace std;

const int N = 2e6+100;
const int mod = 1e9+7;
char a[N];
int num[N];
int pow[N]; 
int sum_pow[N];
int qian[N];
int hou[N];
int sum_qian[N];
signed main(){
	cin>>a;
	int len = strlen(a); 
	pow[0] = 1;
	for(int i = 1 ; i <= len ;i++) {
		pow[i] = (pow[i - 1] * 10) % mod;
		num[i] = a[i-1] - '0';
	}
	for(int i = 1 ; i <= len ;i++) {
		qian[i] = ((qian[i-1] * 10) % mod + num[i]) % mod;
		sum_qian[i] =( sum_qian[i - 1 ] + qian[i] ) % mod;
	}
	for(int i = len ; i >= 1;i-- ) {
		hou[i] = hou[i+1] + ( num[i] * pow[len - i] ) % mod;
	}
	int ans = 0;
	for(int i = len ; i >= 1 ;i--) {
		int number = (((sum_qian[i - 1] * pow[ len - i ]) % mod 
	  + (hou[i + 1] * i) % mod) % mod) % mod;
		ans = (ans + number) % mod;
	} 
	cout << ans % mod;
	return 0;
}

题目传送门

写的前面

  • \(\text{mod}\) 后出现减法算法不加 \(\text{mod}\) 出现负数修了半天我是sb。
  • 在预处理递推过程中会出现负下标,就会出现一些很奇怪的错误,还有就 \(2^0 = 1\)
    其实负下标在我这里没太大的影响,就是会造成\(1\)的精度缺失,因为两个\(\frac{1}{2}\)我都没加上。
  • 注释里其实是有彩蛋的,自己感觉自己写的很对,但一直过不去,就 \(\text{rand()}\)了一下,中间出现了负数,如果一直调不出来可以看一看

solution

  1. 以三层的图为例,题目显然给出的是一颗完全二叉树
    然后枚举以哪个点为\(\text{LCA}\)进行统计答案,以这个思路开始,考虑这个点的贡献。

  2. 发现一号点的贡献是\(1\)号点的(左儿子数 + 1) \(\times\) 右儿子数,左儿子 + 1意义是它加上它自己的贡献
    然后每层依次递推,最终会得出这样一个式子。

\[\sum\limits_{ i = 1}^{k}\left[\left( 2^{k - 2\times i + 3} - 1 \right) \times\left(\frac{\left(2^{i - 1} + 2^{i} -1\right) \times 2^{i - 1}}{2}\right)\right] \]

然后就是很裸的解式子的问题了,我们可以不这个式子做到\(O(n)\)

下面的推导有部分跳步

首先不考虑 \(\sum\) , 而且看着分数很难受,\(\frac{1}{2}\) 可以删掉,把\(2^{i-1}\)乘进去

\[\sum\limits_{ i = 1}^{k}\left[\left( 2^{k - 2\times i + 3} - 1 \right) \times\left(2^{2i - 2} + 2^{2i - 1} -2^{i-1}\right)\right] \]

然后分配率处理

\[\sum\limits_{i = 1}^{k}\left( 2^{k-2} + 2^{k-1} - 2^{k-i-1}-2^{2i-3}-2^{2i-2}+2^{i-2}\right) \]

然后发现里面出现常量了,有的地方可以把\(\sum\)给提出来,最后可以得到一个这样的式子

\[k \times \left( 2^{2k - 2} + 2^{2k-1}\right) - \sum\limits_{i=1} ^{k}2^{k-i-1} - \sum\limits_{i=1}^{k}2^{2i-3} - \sum\limits_{i=1}^{k}2^{2i-2} + \sum\limits_{i=1}^{k} 2^{i-2} \]

显然可以预处理出 \(2^?\)幂以及 \(2^{2i-3}\) 还有\(2^{2i-2}\)以及 \(2\)的幂的前缀和

\(\sum\limits_{i=1}^{k}2^{2k-i-1}\)这一块就是由\(2\)的幂的前缀和得到的,可以化为\(\text{sum}_{2k-2}-\text{sum}_{k-2}\)

\(\text{sum}\)表示2的幂的前缀和

然后我们就可以做到\(O(n)\)的预处理,\(O(1)\)的查询,总的复杂度为\(O(n + T)\)

code

/*
Author:Imy_isLy
知识点: 树形结构 前缀和
*/
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e6+100 * 2;
const int M = 1e6;
const int inf = 2147483647;
const int mod = 998244353;
//=============================================================
int n, k;
int POW[N], sum[N], sum2i_3[N], sum2i_2[N];

//=============================================================
int read() {
  int s = 0 , f = 0 ; char ch = getchar() ;
  while(!isdigit(ch)) f |= (ch == '-') , ch = getchar();
  while(isdigit(ch)) s = s * 10 + (ch ^ 48) , ch = getchar();
  return f ? -s : s;
}
void pre() {
  POW[0] = 1;
  int pp = 1;
  for (int i = 1 ; i <= (M << pp); ++i) {
    POW[i] = (POW[i - 1] << pp) % mod;
    sum[i] = (sum[i - 1] + POW[i] ) % mod;
  }
  for (int i = 1 ; i <= (M << pp); ++i) {
    if(2 * i - 3 >= 0)
    sum2i_3[i] = (sum2i_3[i - 1] + POW[2 * i - 3]) % mod; 
    if(2 * i - 2 >= 0) 
    sum2i_2[i] = (sum2i_2[i - 1] + POW[2 * i - 2]) % mod;
  }
  return ;
}
signed main() {
//  freopen("1.in","r",stdin);
//  freopen("2.out","w",stdout);
//	srand(time(0));
//  int T = rand() % M; 
	int T = read();
  pre();
  while(T--) {
    k = read();
    if(k == 0) {
      puts("0"); continue;
    }
    if(k == 1) {
      puts("1");continue;
    }
    int ans  = 
    ( ( ( ( k * (POW[ 2 * k - 2] + POW[2 * k - 1] ) % mod) % mod
    - (sum[2 * k - 2] % mod - sum[k - 2] % mod + mod ) % mod + mod ) % mod 
    + (-sum2i_3[k] % mod - sum2i_2[k] % mod + mod) % mod 
    + sum[k - 2] % mod ) + mod ) % mod;
    cout << (ans + 1ll) % mod <<"\n";
  }
//  system("pause");
  return 0;
}
posted @ 2020-12-02 22:01  Imy_bisLy  阅读(260)  评论(4编辑  收藏  举报