差分的总结

其实差分就是前缀和的逆运算,通过前缀和的相减可以得到数组的原值

一维差分,给定一个前缀和序列a,我们可以通过计算得到它的差分序列

这里差分序列b定义为: b[1]=a[1],b[i]=a[i]-a[i-1];

我们可以通过差分来实现区间操作,若要把a的区间[l,r]加d,就可以通过b[l]+d,b[r+1]-d的操作实现,

AcWing 797. 差分

#include <bits/stdc++.h>
using namespace std;
const int N=100010;
int a[N],b[N];
int main()
{
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
        b[i]=a[i]-a[i-1];//处理为差分序列
    }
    //差分就是把原数组作为差分的前缀和。
    while(m--)
    {
        int l,r,c;
        cin>>l>>r>>c;
        b[l]+=c;
        b[r+1]-=c;
    }
    for(int i=1;i<=n;i++)
    {
        b[i]+=b[i-1];
        cout<<b[i]<<" ";
    }
    return 0;
}

AcWing 100. IncDec序列

这个题让我们用最少的操作次数使得a数组都一样,转化思想就是让差分序列b都为0,b[1]等于a[1];

我们在做b[l]++,b[r+1]--;操作的时候,要找两个数配对,那么负数++,正数--,是不是就最快了。这就是第一种操作,如果b中同时存在正数和负数,优先考虑这个操作

但是做完之后可能还不行,如果正负数个数不等的话就需要继续操作

然后继续操作非零数

我们假设有正数和为p,负数和为q,那么第一种操作最多会进行min(p,q)次,然后我们继续操作剩余的次数abs(p-q),取绝对值

那最后会有多少种结果?

前面两两配对的操作最后只可能得到一种结果,后面的纯正负需要操作abs(pos-neg)次,b[1]可以是任意数,所以最后就是abs(p-q)+1种结果

#include <bits/stdc++.h>
using namespace std;
const int N=100010;
int a[N],b[N];
int main()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
        b[i]=a[i]-a[i-1];
    }
    long long p=0,q=0;
    for(int i=2;i<=n;i++)
    {
        if(b[i]>0) p+=b[i];
        else q-=b[i];
    }
    cout<<min(q,p)+abs(q-p)<<endl;//这里输出max(q,p)就可以了
    cout<<abs(q-p)+1;
    return 0;
}

AcWing 4262. 空调

这个和上个题是类似的

#include <bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int p[N];
int m[N];
int main()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>p[i];//理想温度数组
    }
    for(int i=1;i<=n;i++)
    {
        int t;
        cin>>t;
        p[i]-=t;//这里得到与理想温度的差值。
        //类似Indec序列,最少次数把p变为0;
    }
    /*
    for(int i=n;i>1;i--)
    {
        p[i]=p[i]-p[i-1];
    }
    */
    for(int i=1;i<=n;i++)
    {
        m[i]=p[i]-p[i-1];
    }
    //然后统计正负数数量
    long long r=0,q=0;
    for(int i=1;i<=n;i++)
    {
        if(m[i]>0) r+=m[i];
        else q-=m[i];
    }
    cout<<max(r,q);
    return 0;
}

AcWing 798. 差分矩阵

二维差分模板题

#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int b[N][N],a[N][N];
void insert(int x1,int y1,int x2,int y2,int c)//实现差分数组
{
    b[x1][y1]+=c;
    b[x2+1][y1]-=c;
    b[x1][y2+1]-=c;
    b[x2+1][y2+1]+=c;
}
int main()
{
    int n,m,q;
    cin>>n>>m>>q;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            cin>>a[i][j];
            insert(i,j,i,j,a[i][j]);
        }
    }
    while(q--)
    {
        int x1,x2,y1,y2,c;
        cin>>x1>>y1>>x2>>y2>>c;
        insert(x1,y1,x2,y2,c);
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
           b[i][j]+=b[i-1][j]+b[i][j-1]-b[i-1][j-1];//求二维前缀和
           cout<<b[i][j]<<" ";
        }
        cout<<endl;
    }
    return 0;
}

洛谷P3397地毯

做完上个题如果怕没有掌握可以做做这个题,意思非常简单

#include <bits/stdc++.h>
using namespace std;
const int N=1010;
int a[N][N];
void insert(int x1,int y1,int x2,int y2,int c)
{
    a[x1][y1]+=c;
    a[x1][y2+1]-=c;
    a[x2+1][y1]-=c;
    a[x2+1][y2+1]+=c;
}
int main()
{
    int n,m;
    cin>>n>>m;
    while(m--)
    {
        int x1,x2,y1,y2;
        int c=1;
        cin>>x1>>y1>>x2>>y2;
        insert(x1,y1,x2,y2,c);
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            a[i][j]+=a[i-1][j]+a[i][j-1]-a[i-1][j-1];
            cout<<a[i][j]<<" ";
        }
        cout<<endl;
    }
    return 0;
}

AcWing 5396. 棋盘

这个思路很好想,二维差分,改变一定区间内数组的值,如果被翻转偶数次就仍为白子,奇数次为黑子

#include <bits/stdc++.h>
using namespace std;
const int N=1010;
int a[N][N];//直接以a作为棋盘的差分矩阵。
int n,m;
void change(int x1,int y1,int x2,int y2,int c)
{
    a[x1][y1]+=c;
    a[x2+1][y1]-=c;
    a[x1][y2+1]-=c;
    a[x2+1][y2+1]+=c;
}
int main()
{
    cin>>n>>m;
    int c=1;
    while(m--)
    {
        int x1,y1,x2,y2;
        cin>>x1>>y1>>x2>>y2;
        change(x1,y1,x2,y2,c);
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            a[i][j]+=a[i-1][j]+a[i][j-1]-a[i-1][j-1];
            if(a[i][j]%2==0) cout<<"0 ";
            else cout<<"1 ";
        }
        cout<<endl;
    }
    return 0;
}

AcWing 4655. 重新排序

这里还用到了排序不等式的知识,不懂得可以去学习一下

这里记录每个位置被计算的次数,然后计算sum1,之后进行排序,也就是让计算次数最大的位置放最大的数,依次递减。

#include<bits/stdc++.h>
using namespace std;
using ll=long long;
const int N=1e5+5;
int a[N];
int b[N];//用来修改每个位置会被计算多少次
int main()
{
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
    }
    int m;
    cin>>m;
    ll sum1=0;
    ll sum2=0;
    while(m--)
    {
        int l,r;
        cin>>l>>r;
        b[l]++;
        b[r+1]--;
    }
    for(int i=1;i<=n;i++) b[i]+=b[i-1];//差分数组求前缀和转为次数数组
    for(int i=1;i<=n;i++)
    {
        sum1+=(ll)b[i]*a[i];
    }
    sort(b+1,b+n+1);
    sort(a+1,a+n+1);
    for(int i=1;i<=n;i++)
    {
        sum2+=(ll)a[i]*b[i];
    }
    cout<<sum2-sum1;
    return 0;
}

AcWing 3729. 改变数组元素

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int a[N];
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        int n;
        cin>>n;
        memset(a,0,(n+1)*4);
        for(int i=1;i<=n;i++){
            int x;
            cin>>x;//x即为题目中的a[i];
            x=min(x,i);
            int l=i-x+1,r=i;
            a[l]++,a[r+1]--;
        }
        for(int i=1;i<=n;i++) a[i]+=a[i-1];
        for(int i=1;i<=n;i++) cout<<!!a[i]<<" ";
        cout<<endl;
    }
    
    return 0;
}

AcWing 101. 最高的牛


posted @ 2025-08-23 17:07  流云鹤=  阅读(13)  评论(0)    收藏  举报