1. 程式人生 > 其它 >題解報告(差分模板)

題解報告(差分模板)

差分演算法:

給定n長度的陣列,有m個請求,每個請求都是在 [l,r](l<=n;r<=n)區間內加上數c,最後輸出該陣列。

若用暴力法求解,需要迴圈m個請求,每個請求都是需要遍歷陣列 (r-l) 遍 ((r-l)<=n)。其中的時間複雜度為O(mn)(資料範圍在10e3左右)。而運用差分演算法,可以將時間複雜度降到線性即O(m+n);(資料範圍可以在10e7左右)

接下來具體講解差分演算法

由此可以看出差分實際上是字首和運算的逆運算。當需要做出在 [l,r] 區間內的元素加上c的操作時,需要做出的操作為 b[ l ]+=c,由於a陣列為b陣列的字首和,所以a陣列在 l 後的元素都會自動加上c。而由於是在閉區間內,大於 r 下標

的元素需要減去相應的c,來抵消前面相加的c。此時的操作為 b[ r+1] -=c; 而這一步操作的時間複雜度為O(m);

在計算好差分陣列後,(在初始化b陣列時,值為0) 如何得到操作後的陣列a,可以有兩個思路:

個人:對於b陣列計算b陣列中的每一個字首和,即遍歷b陣列,b[i]+=b[i-1];這樣子相當於得到了對於每一個下標的操作c的具體值,a陣列想要得到操作後的陣列,只需要和計算字首和後的b陣列對於的下標相加即可

y總:可以將a陣列中的元素可以看成 [l,l] 區間內加入元素 a [ l ] 到b陣列中去,先做一遍初始化。然後再做m個請求操作。最後計算b陣列的每一個字首和即可。

不管哪種方法,實現的時間複雜度為O(n),即陣列的長度,最終的時間複雜度為O(n+m),為線性

注:當 l 和 r 的數值過大,並且差分後的大多數元素並沒有遍歷到的時候,需要用到離散化的方法。

題目:

Acwing 797.差分(模板題)

//個人方法
#include<iostream>
#include<cstring>
using namespace std;
const int MAXN=1e6;
int a[MAXN];
int b[MAXN];
int main()
{
    int n,m;
    cin>>n>>m;
    memset(b,0,sizeof(b));//初始化差分陣列
    for(int i=1
;i<=n;i++) cin>>a[i];//讀入原陣列 for(int i=0;i<m;i++) { int ai,bi,c; cin>>ai>>bi>>c; b[ai]+=c; b[bi+1]-=c; } for(int i=1;i<=n;i++) b[i]+=b[i-1];//計算差分陣列的字首和,使得差分陣列中的對應下標的值為相應的操作後的總值 for(int i=1;i<=n;i++) a[i]+=b[i];//對於a陣列的每一個下標讀入相應的操作 for(int i=1;i<=n;i++) cout<<a[i]<<" "; return 0; }
//y總
#include<iostream>
#include<cstring>
using namespace std;
const int MAXN=1e6;
int a[MAXN];
int b[MAXN];

void insert(int l,int r,int c)
{
    b[l]+=c;
    b[r+1]-=c;
    return ;
}

int main()
{
    int n,m;
    cin>>n>>m;
    memset(b,0,sizeof(b));//初始化差分陣列
    for(int i=1;i<=n;i++) 
    {
        cin>>a[i];//讀入原陣列
        insert(i,i,a[i]);//將原陣列視為在區間[i,i]中加上元素a[i]
    }
    for(int i=0;i<m;i++)
    {
        int ai,bi,c;
        cin>>ai>>bi>>c;
        insert(ai,bi,c);
    }
    for(int i=1;i<=n;i++) b[i]+=b[i-1];//計算差分陣列的字首和,使得差分陣列中的對應下標的值為相應的操作後的總值
for(int i=1;i<=n;i++) cout<<b[i]<<" "; return 0; }

Acwing 2041.乾草堆

該題其實也是模板題,只不過題中的原陣列的全部元素預設為0,只需要得到差分陣列的各個字首和即可,由於需要從小到大排序,並且輸出中間值,在操作後還需要排序。並且輸出最中間的值即下標為(n/2+1)

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int MAXN=1e6+5;
int st[MAXN],d[MAXN];
int main()
{
  int n,k;
  cin>>n>>k;
  //memset(st,0,sizeof(st));
  memset(d,0,sizeof(d));
  for(int i=1;i<=k;i++)
  {
    int l,r;
    cin>>l>>r;
    d[l]++; d[r+1]--;//對於區間新增乾草堆
  }
  for(int i=1;i<=n;i++) d[i]+=d[i-1];//計算字首和
  sort(d+1,d+n+1);
  cout<<d[n/2+1];//計算中間數
}