前缀和与差分之IncDec sequence

本文详细介绍了前缀和与差分的概念及其在算法设计中的应用,包括如何通过预处理实现快速区间查询和区间修改,以及如何利用差分序列进行高效的数据结构更新。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

参考链接:https://blog.youkuaiyun.com/hzk_cpp/article/details/80407014

题目链接:https://www.acwing.com/problem/content/102/

前缀和:

就是一个数组,要快速静态查询区间和,我们只要处理一个数组时A[i]=a[1]+a[2]+...+a[i].

A[1]=a[1]

A[2]=a[1]+a[2]

A[3]=a[1]+a[2]+a[3]

A[4]=a[1]+a[2]+a[3]+a[4]

......

例如:求[2,4]的区间和,A[4]-A[1]=a[2]+a[3]+a[4]=a[1]+a[2]+a[3]+a[4]-(a[1])

那么查询区间[l,r]的时候只要输出A[r]-A[l-1].

那么这是时候预处理是O(n)的,查询一次是O(1).

在很多情况下这种算法都是可行的,但是必须满足区间减法的性质.

代码预处理如下:

for (int i=1;i<=n;i++)
A[i]=A[i-1]+a[i];
差分:

只不过它可以配套前缀和使用.

差分是一种很巧妙的思想,它的主要用途是维护一个区间的快速修改(加减),但查询是单次的时候.O(n+m)

这种思想的主体就是维护一个数组A,其中A[i]=原数组a[i]-a[i-1].

那么就是刚好与前缀和有点像.

也是互逆的运算.

那么这怎么快速维护呢?

也就是说给区间[l,r]+num.

那么就是给开头A[l]+num,再给结尾A[r+1]-num.

很好理解啊.

那么实现如下:

void add(int l,int r,int num){
A[l]+=num;A[r+1]-=num;
}
那么最后需要查询了,就这样处理一下:

for (int i=1;i<=n;i++)
a[i]=a[i-1]+A[i];
那么就是原来的数组了.

时间复杂度O(1)修改,O(n)查询.

 

差分就是将数列中的每一项分别与前一项数做差,例如:

一个序列1 2 5 4 7 3,差分后得到1 1 3 -1 3 -4 -3

这里注意得到的差分序列第一个数和原来的第一个数一样(相当于第一个数减0)

差分序列最后比原序列多一个数(相当于0减最后一个数)

性质:

1、差分序列求前缀和可得原序列

2、将原序列区间[L,R]中的元素全部+1,可以转化操作为差分序列L处+1,R+1处-1

3、按照性质2得到,每次修改原序列一个区间+1,那么每次差分序列修改处增加的和减少的相同

public class Main {
    //前缀和
    public static void fun(int arr[],int A[]){
        /*
        A[1]=a[1]

        A[2]=a[1]+a[2]

        A[3]=a[1]+a[2]+a[3]

        A[4]=a[1]+a[2]+a[3]+a[4]
        */
        for (int i=1;i<arr.length;i++)
            A[i]=A[i-1]+arr[i];
        
    }
    public static int query(int l,int r,int A[]){
        //查询区间[l,r]的时候只要输出A[r]-A[l-1]
        return A[r]-A[l-1];
    }
    //差分
    public static void fun2(int arr[],int A[]){
        for (int i=1;i<arr.length;i++)
            A[i]=arr[i]-arr[i-1];
    }
    public static void add(int l,int r,int num,int A[],int arr[]){
        A[l]+=num;A[r+1]-=num;
        for (int i=1;i<A.length;i++)
            arr[i]=arr[i-1]+A[i];
    }
    
    public static void main(String[] args) {
        int arr[]={0,1,2,3,4};
        int A[]=new int[5];
//        fun(arr, A);
//        System.out.println(query(1, 3, A));
//        System.out.println(query(2, 4, A));
        
        fun2(arr, A);
        add(1, 3, 9, A, arr);
        for (int i = 1; i < arr.length; i++) {
            System.out.println(arr[i]);
        }
    }

}

IncDec sequence 题解

 对于一个给定的数列arr,它的差分数列A定义为, A[1]=arr[1],A[i]=arr[i]−arr[i−1](2<=i<=n) 即:A[1]=arr[1],A[i]=arr[i]−arr[i−1](2<=i<=n)

性质,把序列arr的区间[L,R]加d,也就是把arr[l],arr[l+1]........arr[r]都加上d,其实就是它的差分序列B中,A[l]+d,A[r+1]−d其他的位置统统不改变。
因此在这道题目中,我们就可以利用这个性质,因为我们只要求arr序列中所有的数相同,而不在意这些方案具体是什么,所以说我们就可以转化题目,也就是将对arr序列的+1,−1+1,−1操作,让arr序列相同,改成目标把A2,…,An变成全0即可(因为A[1]=arr[1],所以是A2~A[n]变成0),也就是arr序列全部相等。而且最后得到的arr序列,就是这n个A1
因为我们有上面所说的性质,那么我们就可以,每一次选取Ai和Aj,2<=i,j<=n而且这两个数,一个为正数,一个为负数,至于为什么要是正负配对,因为我们是要这个A序列2~n都要为0,所以这样负数增加,正数减少,就可以最快地到达目标为0的状态。
至于那些无法配对的数Ak可以选A1,进行修改。即Ak+d,A1-k,A1不影响全局操作
所以说我们这道题目得出的答案就是,最少操作数 min(p,q)+abs(p−q),然后最终序列a可能会有abs(p−q)+1种情况。p为b序列中正数之和,而q为b序列中负数之和

package 差分与前缀和;

import java.util.Scanner;

public class Main1 {
    public static void fun2(long arr[],long A[]){
        for (int i=1;i<arr.length;i++)
            A[i]=arr[i]-arr[i-1];
    }
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt();
        long arr[]=new long[n+1];
        for (int i = 1; i < arr.length; i++) {
            arr[i]=sc.nextLong();
        }
        long A[]=new long[n+1];
        fun2(arr, A);
        long positiveNumber=0;
        long negativeNumber=0;
        for (int i = 2; i < A.length; i++) {
            if (A[i]>=0) {
                positiveNumber+=A[i];
            }else{
                negativeNumber-=A[i];
            }
        }
        System.out.println(Math.abs(positiveNumber-negativeNumber)+Math.min(negativeNumber, positiveNumber));
        System.out.println(Math.abs(positiveNumber-negativeNumber)+1);
    }

}

 

转载于:https://www.cnblogs.com/clarencezzh/p/10315145.html

### Increment Decrement Sequence Implementation In programming, an increment-decrement sequence typically refers to operations that involve increasing or decreasing values by a fixed amount iteratively. This concept can be implemented using loops and arithmetic operators. A common way to implement such sequences involves initializing variables with start values and then applying increments or decrements within loop structures: ```cpp // C++ example demonstrating inc/dec sequence #include <iostream> int main() { int value = 0; // Initial value while (value < 5) { // Loop condition std::cout << "Incrementing: " << ++value << '\n'; // Pre-increment operation } while (value > 0) { std::cout << "Decrementing: " << --value << '\n'; // Pre-decrement operation } return 0; } ``` For more complex scenarios involving collections like arrays or vectors, iterator-based approaches provide flexibility when traversing elements forward or backward[^2]: ```cpp std::vector<int> numbers{1, 2, 3, 4}; for(auto it = numbers.begin(); it != numbers.end(); ++it){ *it += 1; // Increment each element via dereferenced iterator } for(auto rit = numbers.rbegin(); rit != numbers.rend(); ++rit){ *rit -= 1; // Decrement from reverse direction similarly } ``` Iterators allow for efficient traversal over container types without exposing underlying data structure details. When working with standard template library containers, iterators offer powerful ways to manipulate sequences through generic algorithms provided by the C++ Standard Library. Regarding specific implementations of `incdec` as mentioned in some contexts, these might refer to specialized functions designed around particular requirements not covered here directly but following similar principles outlined above.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值