算法------(6)差分

例题:(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;
}

         

          

  • 8
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值