前缀积与后缀积


引入

通过一道例题引入前缀积和后缀积的用法

Description:
给出数列A1,A2,…,AN,并设
Bi = (A1 * A2 * A3 … AN) / Ai mod (109 + 7)
现要求把所有的Bi 算出来

Input:
输入包含多组测试数据。对于每组数据,第1 行,1 个整数N(1 ≤N≤100,000), 表示数列的长度。第2行,N 个整数A1,A2,…,AN(1 ≤Ai≤109),表示给出的数列。输入以一个 0 表示结尾。

Output:
对于每组数据,输出一行,N 个整数用空格分隔,表示算出的B1,B2,…,BN。

Sample Input:
3
1 2 3
0

Sample Output:
6 3 2

题目给的数据很大,如果一直乘下去,肯定会发生算术溢出。因此我们需要利用用取余的分配律 :

(a*b)%c = (a%c * b%c)%c

这道题当然可以通过暴力求解。但如果要求你不用除法,并且在常数时间内得到题解呢?
那就要用到我们所说的前缀积和后缀积了。


一、什么是前缀积和后缀积

对于给定的数组A[1…n] :

  • 前缀积:新建一数组B,数组中每一项B[i]保存A中[1…i]的积;即数组A前 i 项的积。
  • 后缀积:新建一数组B,数组中每一项B[i]保存A中[i…n]的积;即数组A后 n-i 项的积。

二、如何使用前缀积后缀积

1.思路

对于上述例题,在无法使用除法的情况下,我们需要将思路转换一下:
 想要得到 Bi = (A1 * A2 * A3 … AN) / Ai mod (109 + 7) 的值,除了求出数组A中所有数的积并且分别取模之后再除以Ai以外,我们还可以将Bi看做两个区块:即A[1…i-1]和A[i+1…n]的乘积对(109 + 7)取模。
 也就是A数组中前i-1个数的前缀积 pre[i-1] 乘以 A数组中第i+1到n个数的后缀积 suff[i+1] 的结果取模后的值。

Bi = (pre[i-1]*suff[i+1]) % 1000000007

2.前缀积后缀积的计算

 已知数组A和其大小n之后,由前缀积和后缀积的概念可得:
前缀积:

	pre[0] = 1 ;
	for( int i=1 ; i<=n ; i++ )
        pre[i] = ( pre[i-1]*a[i] ) % 1000000007 ;

后缀积:

	suff[n+1] = 1 ;
	for( int i=n ; i>=1 ; i-- )
        suff[i] = ( suff[i+1]*a[i] ) % 1000000007 ;

3.完整代码(C++)

#include <stdio.h>

#define MOD 1000000007
#define maxn 100002 ;

int main()
{
    int n ;
    int pre[maxn] , suff[maxn] ;
    int a[maxn] ;

    for( int i=1 ; i<=n ; i++ )
        scanf( "%d" , &a[i] ) ;

    pre[0] = suff[n+1] = 1 ;

    for( int i=1 ; i<=n ; i++ )
        pre[i] = ( pre[i-1]*a[i] ) % MOD ;  // pre[i]为数列a中前i个数的乘积

    for( int i=n ; i>=1 ; i-- )
        suff[i] = ( suff[i+1]*a[i] ) % MOD ;    // suff[i]为数组中后n-i个数的乘积

    for( int i=1 ; i<=n ; i++ )
        printf( "%d%c" , (pre[i-1]*suff[i+1])%MOD , (i==n)?'\n':' ' ) ;
            // pre[i-1]*suff[i+1]为a数组除去a[i]之后,其余成员的乘积 

    return 0 ;
}

前缀和与后缀和

 与前缀积后缀积思路类似:

  • 前缀和:新建一数组B,数组中每一项B[i]保存A中[0…i]的和;
  • 后缀和:新建一数组B,数组中每一项B[i]保存A中[i…n-1]的和;

其他例题

1.求数组中连续一段和,使其绝对值最小

思路:
前缀和的性质:a[i]+a[i+1]+…+a[j]=sum[j]-sum[i-1]
将前缀和排序,取最小的两个


2.把一个数组从中间p位置分开,使得a[0]+…+a[p-1]与a[p]+a[p+1]+…+a[n-1]差值最小

思路:
差值 = | p-1的前缀和-p的后缀和 | ;
记录p从0至n-1扫过时,最小的差值即为题解。


参考文章:
前缀积和后缀积-deqip44248
前缀和、前缀积-webcandy

好的,下面是一个简单的实现: ```c++ #include <iostream> using namespace std; class Matrix { private: int data[3][3]; public: Matrix(int arr[3][3]) { for(int i=0; i<3; i++) { for(int j=0; j<3; j++) { data[i][j] = arr[i][j]; } } } void print() { for(int i=0; i<3; i++) { for(int j=0; j<3; j++) { cout << data[i][j] << " "; } cout << endl; } } friend bool operator!=(const Matrix& m1, const Matrix& m2) { for(int i=0; i<3; i++) { for(int j=0; j<3; j++) { if(m1.data[i][j] != m2.data[i][j]) return true; } } return false; } friend Matrix operator+(const Matrix& m1, const Matrix& m2) { int arr[3][3]; for(int i=0; i<3; i++) { for(int j=0; j<3; j++) { arr[i][j] = m1.data[i][j] + m2.data[i][j]; } } return Matrix(arr); } friend Matrix operator*(const Matrix& m1, const Matrix& m2) { int arr[3][3]; for(int i=0; i<3; i++) { for(int j=0; j<3; j++) { arr[i][j] = 0; for(int k=0; k<3; k++) { arr[i][j] += m1.data[i][k] * m2.data[k][j]; } } } return Matrix(arr); } Matrix& operator++() { for(int i=0; i<3; i++) { for(int j=0; j<3; j++) { data[i][j]++; } } return *this; } Matrix operator++(int) { Matrix temp(*this); operator++(); return temp; } }; int main() { int a1[3][3] = {{1,2,3}, {4,5,6}, {7,8,9}}; int a2[3][3] = {{1,2,3}, {4,5,6}, {7,8,10}}; Matrix m1(a1); Matrix m2(a2); if(m1 != m2) cout << "m1 != m2" << endl; else cout << "m1 == m2" << endl; Matrix m3 = m1 + m2; m3.print(); Matrix m4 = m1 * m2; m4.print(); m1.print(); m1++; m1.print(); ++m1; m1.print(); return 0; } ``` 注意,这只是一个简单的实现,可能并不完善。如果需要更完整的实现,还需要考虑一些边界情况和异常处理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值