zoukankan      html  css  js  c++  java
  • 前缀和

    前言

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

    前缀和聚集地

    定义一个函数
    (s(l,r) = sumlimits_{i = l}^{r}a_i imes sumlimits_{i = l}^{r}b_i)
    要求

    (sumlimits_{l = 1}^{n}sumlimits_{r = l}^{n}s(l,r))

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

    [sumlimits_{l = 1}^{n}sumlimits_{r = 1}^{n}(sumlimits_{i = l}^{r}a_i imes sumlimits_{i = l}^{r}b_i) ]

    2. 进一步讲里面的化简

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

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

    [sumlimits_{l = 1}^{n}sumlimits_{r = 1}^{n}[sumlimits_{i = l}^{r}a_i imes(sumb[r]-sumb[l-1])] ]

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

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

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

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

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

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

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

    (sumlimits_{ l = 1}^{n}(sumlimits_{r = l}^{n}(suma[r] imes sumb[r] + suma[l-1] imes sumb[l-1] - suma[r] imes sumb[l - 1] - sumb[r] imes suma[l - 1]))

    6.式子就变成了

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

    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)个整数,求

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

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

    得到

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

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

    [sumlimits_{i = 1} ^ {n}[sumlimits_{ 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 imes(n-1) - sumlimits_{i = 1}^{n}a_i imes (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)
    • 每一位上的数( imes)之后为(ten[i])

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

    Solution :

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

    [egin{aligned} &sum_{i=1}^{x-1}{left{ 10^{n-x} imes f(1,i) +f(x+1,n) ight}}\ =& (x-1) imes f(x+1,n) + 10^{n-x} imes 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
    ", 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;
    }
    
  • 相关阅读:
    find命令 -- 之查找指定时间内修改过的文件
    nginx
    lighttpd 搭建
    mysql主从复制5.6基于GID及多线程的复制笔记
    centos下MySQL主从同步配置
    数据库集群搭建
    linux 系统监控、诊断工具之 top 详解
    Linux下Apache并发连接数和带宽控制
    DXGI屏幕捕捉
    CUDA以及CUDNN安装配置(WIN10为例)
  • 原文地址:https://www.cnblogs.com/-wzd233/p/14076796.html
Copyright © 2011-2022 走看看