前缀和与差分

一维前缀和

从数组第一个开始累加

s[i]=s[i-1]+a[i]

求区间[l,r]的和,O(1)复杂度 sum=s[r]-s[l-1]

二维前缀和

递推 s[i][j]=s[i][j]-s[i-1][j]-s[i][j-1]+s[i-1][j-1]

例题:激光炸弹

一种新型的激光炸弹,可以摧毁一个边长为R的正方形内的所有的目标。现在地图上有n(n≤10000)个目标,用整数xi,yi(0≤xi,yi≤5000)表示目标在地图上的位置,每个目标都有一个价值0<vi<100。激光炸弹的投放是通过卫星定位的,但其有一个缺点,就是其爆破范围,即那个边长为R的正方形的边必须和x,y轴平行。若目标位于爆破正方形的边上,该目标将不会被摧毁


将点向右下移动一次就可解决边界不包括的点
给出一个5000×5000的矩阵,每个格子上都有权值,求用一个n×n的框最多能框住多少?

用前缀和来求n*n的框的价值
value=s[i][j]+s[i-r][j]+s[i][j-r]-s[i-r][j-r]);

#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
const int maxn=5005;
int s[maxn][maxn];
int main(){
    int n,r;
 //   memset(s,0,sizeof s);
    freopen("a.txt","r",stdin);
    cin>>n>>r;//n个数,正方形半径为r
// 没有注意到相同点会出现多次
//    for(int i=0;i<n;i++){
//        int a,b;
//        cin>>a>>b;
//        cin>>s[a+1][b+1];//向右下方移动1位
//    }
	for(int i=1;i<=n;i++){
		int a,b,v;
		cin>>a>>b>>v;
		sum[a+1][b+1]+=v;
	}
    for(int i=1;i<=5001;i++){//第0默认为0
        for(int j=1;j<=5001;j++){
            s[i][j]=s[i][j]+s[i-1][j]+s[i][j-1]-s[i-1][j-1];//二维前缀和
        }
    }
    int ans=0;
    for(int i=r;i<=5001;i++){
        for(int j=r;j<=5001;j++){
            ans=max(ans,s[i][j]-s[i-r][j]-s[i][j-r]+s[i-r][j-r]);//遍历所有存在的子区域
        }
    }
    cout<<ans;
    return 0;
}

差分

差分与前缀和是一种互逆的运算

数组21465
前缀和2371318
差分2-132-1
  • 差分前缀和是原数组
  • 前缀和差分也是原数组

结论题

IncDec sequence(inc序列)

给定一个长度为n(n<= 1 0 5 10^5 105)的数列,每次选择区间 [l,r],使区间内的数加1或减1.
求最少需要多少次操作使的数列中所有的数都一样,并且数列可能有几种?

区间操作放在差分数组中就是改变2个数组的值,目标是,差分数组 b 2 b_2 b2~ b n b_n bn都为0,且数列最终由n个 b 1 b_1 b1组成的,那么不同的 b 1 b_1 b1出现的次数代表了不同的数列

四种操作
  • b 2 b_2 b2~ b n b_n bn中选两个分别+1,-1,或者-1,+1,减少正负数,使差分数组向0移动, (更快接近结果)
  • b 1 b_1 b1 b j b_j bj (2<=j<=n),改变差分数组中某一个数( b 1 b_1 b1改变影响最终不同数列的次数,不影响操作的次数),
  • 同理,选 b j b_j bj b n + 1 b_{n+1} bn+1, b n + 1 b_{n+1} bn+1代表一直改变到原数列的最后一个,所以还是改变了差分数组的一个值
  • 改变 b 1 b_1 b1 b n + 1 b_{n+1} bn+1,原数组全部+,- 1,没有实际意义
  1. b 2 b_2 b2~ b n b_n bn正数总和p,负数绝对值总和q,(原则是走最少),第一步走min(q,p),剩下|q-p|个正数或者负数,
  2. 剩下的|p-q|执行2,3操作,那么恰好为|p-q|次
操作次数
ans=min(q,p)+|q-p|

min(p,q)+|q-p|=max(q,p)

证明

  1. p>qmin(p,q)=q |q-p|=p-q 得到ans=q+p-q=p
    p=max(p,q)
  2. 同理 p<q
数列个数

b 1 b_1 b1一开始为1种,若2,3操作都为2操作,最多可能有|p-q|+1

#include<iostream>
#include<cmath>
using namespace std;
const int maxn=pow(10,5)+1;
int a[maxn];
int b[maxn];
int main(){
    int n;
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        b[i]=a[i];
        b[i]-=b[i-1];
    }
//    for(int i=1;i<=n;i++)
//        cout<<b[i]<< ' ';
    int sum_1=0,sum_2=0;
    for(int i=2;i<=n;i++){//b2开始统计正负
        if(b[i]>0)  sum_1+=b[i];
        else sum_2+=b[i];//0无所谓
    }
    cout<<"操作次数"<<max(sum_1,sum_2);
    cout<<"\n可能的不同数列个数"<<1+abs(sum_1-sum_2);
    return 0;
}

Tallest Cow 最高奶牛 POJ3263

大意:N头牛站成一行,当且仅当2头牛之间所有牛都比它们矮,2头牛能相互看见,一直第p头牛最高,高为H,剩余N-1头牛不知道,给出m对关系(2头牛能够相互看见),求每头牛的最大可能身高? (1<=n,m<= 1 0 4 10^4 104,1<=h<= 1 0 6 10^6 106)

思路:因为i,j,两头牛能够相互看见,那么(i-1)~(j-1)之间的所有牛的身高比他们小,假设所有牛的身高为0,那么(i-1)~(j-1)之间牛的身高都-1,即b[i+1]-=1 b[j]+=1 最后通过前缀和求出原数组,每头牛可能的最高身高=H+a[i]

#include<iostream>
#include<map>
using namespace std;
const int maxn=10005;
int a[maxn];
int b[maxn];
map<pair<int,int>,bool> e;
int main(){
    int n,p,h,m;
    cin>>n>>p>>h>>m;
    for(int i=1;i<=m;i++){
        int k1,k2;
        cin>>k1>>k2;
        if(k1>k2)   swap(k1,k2);
        if( e[make_pair(k1,k2)] == true )    continue;
        else{
            e[make_pair(k1,k2)]=true;
            b[k1+1]--;
            b[k2]++;
        }
    }
    for(int i=1;i<=n;i++){
        a[i]=a[i-1]+b[i];
        cout<<a[i]+h<<endl;
    }
    return 0;
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值