前缀和 二维前缀和 差分 HDU6514

前缀和:前缀和顾名思义就是前面i个数的总和

a[0]=0;
for(int i=1;i<=n;i++)a[i]+=a[i-1];

给出一串长度为n的数列a1,a2,a3…an,再给出m个询问,每次询问给出L,R两个数,要求给出区间[L,R]里的数的和
数组a在经过这样的操作之后,对于每次的询问,我们只需要计算a[R]-a[L-1]。
二维前缀和:通过画图很容易就能知道:假如我想求a[3][4]的前缀和,我得先加上a[2][4]的前缀和,再加上a[3][3]的前缀和,然后这个时候我们发现实际上a[2][3]这个部分我们加了两遍,所以我们需要再减去一遍a[2][3],于是得出公式a[i][j]+=a[i][j-1]+a[i-1][j]-a[i-1][j-1]。

for(int i=1;i<=n;i++){
	for(int j=1;j<=m;j++)
	a[i][j]+=a[i][j-1]+a[i-1][j]-a[i-1][j-1];
} 

应用:给定一个n*m大小的矩阵a,有q次询问,每次询问给定x1,y1,x2,y2四个数,求以(x1,y1)为左上角坐标和(x2,y2)为右下角坐标的子矩阵的所有元素和。注意仍然包含左上角和右下角的元素。

int a[maxn][maxn];
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];
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++)
		a[i][j]+=a[i][j-1]+a[i-1][j]-a[i-1][j-1];
	}
	for(int i=1;i<=q;i++){
		int x1,y1,x2,y2;
		cin>>x1>>y1>>x2>>y2;
		int ans=a[x2][y2]-a[x1-1][y2]-a[x2][y1-1]+a[x1-1][y1-1];
		cout<<ans<<endl;
	}
}

差分:给你一串长度为n的数列a1,a2,a3…an,要求对a[L]~a[R]进行m次操作:
1:将a[L]~a[R]内的元素都加上P
2:将a[L]~a[R]内的元素都减去P
最后再给出一个询问求a[L]-a[R]内的元素之和
最简单的做法:于m次操作每次都遍历一遍a[L]~a[R],给区间里的数都加上P或减去P,最后再求一次前缀和
可是当n,m很大时就会超时,这时候就用到差分了。

int a[maxn],b[maxn];
int main()
{
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	for(int i=1;i<=m;i++){
		int L,R,t,p;
		cin>>t>>L>>R>>p;
		if(t==1){
			b[L]+=p;b[R+1]-=p; 
		}
		else{
			b[L]-=p;b[R+1]+=p;
		}
	}
	int add=0;
	for(int i=1;i<=n;i++){
		add+=b[i];
		a[i]+=a[i-1]+add;
	}
	int x,y;
	cin>>x>>y;
	cout<<a[y]-a[x-1]<<endl;
}

为什么操作1时b[R+1]要减去p呢?因为操作一我只需对[L,R]区间里的数加p,[R+1,n]这个区间里的数没加p,所以需要减掉p。
差分在二维前缀和里的应用:

for(int i=0;i<m;i++){
	int x1,y1,x2,y2,p;
	cin>>x1>>y1>>x2>>y2>>p;
	b[x1][y1]+=p;b[x2+1][y2+1]+=p;
	b[x2+1][y1]-=p;b[x1][y2+1]-=p;
}

HDU 6514
只要把监控能看到的地方标记成1
给盗贼范围的时候看看盗贼范围内每个格子是否是1就能判断能否看到全部盗贼
解决方法是先用二维差分标记监控范围
然后求前缀和
然后把所有大于1的格子改成1
再进行一次前缀和
判断的时候只要求出盗贼格子的数量是否等于这些格子的和

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;
int main()
{
    int n,m;
    while(~scanf("%d%d",&n,&m))
    {
        m=m+7;
        n=n+7;
        int ant[n][m];
        memset(ant,0,sizeof(ant));
        int p;
        scanf("%d",&p);
        int x1,x2,y1,y2;
        while(p--)
        {
            scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
            ant[x1][y1]++;
            ant[x2+1][y2+1]++;
            ant[x2+1][y1]--;
            ant[x1][y2+1]--;
        }
        for(int i=1;i<n;i++)
            for(int j=1;j<m;j++)
                ant[i][j]+=ant[i-1][j]+ant[i][j-1]-ant[i-1][j-1];
        for(int i=1;i<n;i++)
            for(int j=1;j<m;j++)
                if(ant[i][j])ant[i][j]=1;
        for(int i=1;i<n;i++)
            for(int j=1;j<m;j++)
                ant[i][j]+=ant[i-1][j]+ant[i][j-1]-ant[i-1][j-1];
        scanf("%d",&p);
        while(p--)
        {
            scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
            int sum=ant[x2][y2]+ant[x1-1][y1-1]-ant[x1-1][y2]-ant[x2][y1-1];
            int q=(x2-x1+1)*(y2-y1+1);
            if(sum>=q)printf("YES\n");
            else printf("NO\n");
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值