例题:(1)Acwing 797.差分
差分,本质上就是把一个已有的数组当做前缀和数组,然后求原数组。因此原数组中的每个元素都等于a[i+1]-a[i]。
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 100010;
int a[N],b[N];
void insert(int l,int r,int c){
b[l] += c;
b[r+1] -= c;
}
int main()
{
int n,m;
scanf("%d%d", &n, &m);
for(int i = 1;i<=n;i++){
scanf("%d", &a[i]);
insert(i,i,a[i]);
}
for(int i = 1;i<=m;i++){
int l,r,c;
scanf("%d%d%d", &l, &r,&c);
insert(l,r,c);
}
for(int i = 1;i<=n;i++){
a[i] = a[i-1] + b[i];
printf("%d ",a[i]);
}
return 0;
}
练习:(1)洛谷P4552 [Poetize6] IncDec Sequence
本题的题意可以转化为其差分数组从第二个数开始所有数都为0,因为每个下标的前缀和都一样。因此我们只要先求出差分数组,然后看变化多少次可以使其为0。为了使次数最少,我们一定会先把正的和负的同时消去,然后再把多余的正的/负的与a[0]消去,这样就可以得到从第二个下标开始全部为0的结果。当然,情况个数便是在多余的正的基础上再加1。不开long long,****。
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 100010;
long long a[N],b[N];
void insert(long long l,long long r,long long c){
b[l] += c;
b[r+1] -= c;
}
int main(int argc, char** argv) {
long long n,cnt1 = 0,cnt2 = 0;
scanf("%d",&n);
for(int i = 1;i<=n;i++){
scanf("%lld",&a[i]);
insert(i,i,a[i]);
}
for(int i = 2;i<=n;i++){
if(b[i]>0) cnt1+=b[i];
else if(b[i]<0) cnt2-=b[i];
}
printf("%lld\n%lld",max(cnt1,cnt2),abs(cnt1-cnt2)+1);
return 0;
}
(2) AcWing 3729. 改变数组元素
CF1501B,不过用差分可以在acwing上过但是cf会mle。。。。。
用差分记录数组被操作过的次数,如果最后前缀和>=1则元素为1,否则为0。计算差分数组时要注意左端点是否超出数组界限。
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 2e5+10;
int b[N];
void add(int l,int r){
b[l] += 1;
b[r+1] -= 1;
}
int main(){
int t;
scanf("%d", &t);
while(t--){
memset(b,0,sizeof(b));
int n,m,sum = 0;
scanf("%d", &n);
for(int i = 1;i<=n;i++){
int k;
scanf("%d",&k);
k = min(k,i);
add(i - k + 1,i);
}
for(int i = 1;i<=n;i++){
sum += b[i];
printf("%d ",sum?1:0);
}
puts("");
}
return 0;
}
(3)P2879 Tallest Cow S
先把每头牛的高度全部变成最高,然后利用每一条信息把每两头牛中间的牛的高度至少减1。这样就可以转化为差分数组的问题。
本题的题面翻译存在问题,并不保证b奶牛的高度一定大于等于a奶牛的高度,只能确定a奶牛和b奶牛中间的所有奶牛的高度都要比a,b奶牛小。本题的另一个要点是去重,因为题目可能会出现类似(7,3)和(3,7)的数据,因此还需要一个二维数组判重。 每次让小的那个区间在前。
#include <iostream>
using namespace std;
const int N = 1e4;
bool st[N][N];
int b[N];
void add(int l,int r,int w){
b[l] += w;
b[r+1] -= w;
}
int main(int argc, char** argv) {
int n,t,h,m;
scanf("%d%d%d%d",&n,&t,&h,&m);
b[1] = h;
for(int i = 1;i<=m;i++){
int l,r;
scanf("%d%d",&l,&r);
if(l>r) swap(l,r);
if(!st[l][r]){
st[l][r] = 1;
add(l+1,r-1,-1);
}
else continue;
}
int sum = 0;
for(int i = 1;i<=n;i++){
sum += b[i];
printf("%d\n",sum);
}
return 0;
}
(4)CF295A Greg and Array
两次差分可解。第一次差分是针对每一个操作做多少次的数组,每一个区间加1次,第二次差分是针对操作,每一个区间加操作次数*每一次操作加的数。
不开long long见祖宗!!!!开了一半也要见!!!!
#include <iostream>
using namespace std;
const int N = 1e5+10;
typedef long long ll;
ll l[N],r[N],w[N],cnt[N];
ll a[N];
void add1(ll l,ll r,ll w){
a[l] += w;
a[r+1] -= w;
}
void add2(ll l,ll r){
cnt[l] += 1;
cnt[r+1] -= 1;
}
int main(int argc, char** argv) {
ll n,m,k;
scanf("%lld%lld%lld",&n,&m,&k);
for(int i = 1;i<=n;i++){
ll x;
scanf("%lld",&x);
add1(i,i,x);
}
for(int i = 1;i<=m;i++){
scanf("%lld%lld%lld",&l[i],&r[i],&w[i]);
}
for(int i = 1;i<=k;i++){
ll left,right;
scanf("%lld%lld",&left,&right);
add2(left,right);
}
ll sum1 = 0;
for(int i = 1;i<=m;i++){
sum1 += cnt[i];
add1(l[i],r[i],w[i]*sum1);
}
ll sum2 = 0;
for(int i = 1;i<=n;i++){
sum2+=a[i];
printf("%lld ",sum2);
}
return 0;
}
(5) Acwing 1952.金发姑娘和 N 头牛
之前做离散化的时候就碰到了,当时还没复习差分因此没做。现在复习了,还是没做出来。。。。。
首先,我们要求出得到牛奶量的最大值,就是要求出从最小温度到最大温度中,每一个温度的产奶量中的最大值。这样便可把问题转化为,每头牛在不同温度区间上加上一定量的奶数,球最后每个温度产奶量的最大值,因此可以考虑差分。在差分过程中,由于温度的范围较大但温度的个数较少,而我们并不关心温度的具体值是什么,只关心温度间的相对大小。因此只需要把温度离散化到一个小区间然后对区间进行差分即可。
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int N = 200100;
typedef pair<int, int> pii;
vector<pii> add;
vector<int> alls;
int k[N];
void adding(int l,int r,int x){
k[l] += x;
k[r+1] -= x;
}
int find(int x){
int l = 0,r = alls.size() - 1;
while(l<r){
int mid = (l+r)/2;
if(alls[mid]>=x) r = mid;
else l = mid+1;
}
return r+1;
}
int main()
{
int n,a,b,c;
int res = 0;
scanf("%d%d%d%d", &n, &a,&b,&c);
for(int i = 1;i<=n;i++){
int l,r;
scanf("%d%d", &l, &r);
alls.push_back(l);
alls.push_back(r);
add.push_back({l,r});
}
sort(alls.begin(),alls.end());
alls.erase(unique(alls.begin(),alls.end()),alls.end());
int s = alls.size();
for(auto f : add){
int l = find(f.first),r = find(f.second);
adding(0,l-1,a);
adding(l,r,b);
adding(r+1,s-1,c);
}
int sum = 0;
for(int i = 0;i<s;i++){
sum += k[i];
res = max(res,sum);
}
printf("%d",res);
return 0;
}