差分与前缀和1——2021-01-24更

差分与前缀和1

差分

T1 堆叠草堆

题目:

Bessie对她自己最近在农场周围的恶作剧感到抱歉,于是她同意帮助Farmer John堆叠新送达的一批干草捆。
开始时有N(N是奇数)个空草堆,标记为1…N。FJ将会给Bessie包含K条指令的序列,每条指令的格式为"A B",表示Bessie应该在草堆A…B中每一个草堆顶部新放一捆干草。例如,如果Bessie听到指令"10 13",那么她应该在草堆10,11,12和13上各新放一捆干草。
在Bessie完成了FJ的所有指令后,FJ想要知道N个草堆高度的中位数——也就是说,所有草堆排序后中央草堆(由于N是奇数,这个草堆是唯一的)的高度。请帮助Bessie确定FJ问题的答案。
输入
第1行:两个被空格分隔的整数N和K。
第2…1+K行:每行包含一条FJ的指令,其格式为两个被空格分隔的整数A和B。
输出
一个整数, Bessie 完成所有指令后草堆高度的中位数。
输入样例
7 4
5 5
2 4
4 6
3 5
输出样例
1

思路:

看到这个题目后,我第一时间是想到用循环把每次输入的草堆挨个加1,但后来发现TLE了,就想起了用差分,在每次要加的第一个草堆处加1,再最后一个的后面一个-1,最后统一加回去,十分快速简便。

代码如下:

#include<bits/stdc++.h> 
using namespace std;
int n,q,h,a[1000010],b[1000010];

void insert(int l,int r)
{
    
    
	b[l]+=1;
	b[r+1]-=1;
}   //差分函数
int main()
{
    
    
	ios::sync_with_stdio(false);//加快输入输出
	cin>>n>>q;
	for(int i=1;i<=q;i++)
	{
    
    
		int l,r;
		cin>>l>>r;
		insert(l,r);//对l~r的区间草堆加1
	}	
	for(int i=1;i<=n;i++)
		b[i]+=b[i-1];//前缀和
	sort(b+1,b+1+n);
	cout<<b[n/2+1];	
	return 0;
}

T2 套圈游戏

题目:

小b在逛公园的时候发现了一个好玩的游戏,有一个 n*n 的方格,每个方格里面有若干硬币,你只要交完钱,老板就给你 m 个方形的圈,你扔出去的圈会覆盖部分方格,这些方格你可以从中获得一枚硬币,但是老板不会做亏本生意的,他规定:如果一个方格被覆盖2次或以上,则不能获得这个方格中的硬币。

现在已知小b扔出去的 m 个圈的左上角以及右下角坐标,请计算出小b最终获得多少枚硬币。
输入
第一行,两个整数n和m。(2<=n<=2000)
接下来m行,每行4个数,分别表示圈的左上角坐标以及右下角坐标。(坐标值均在1~n之间)
输出
一个整数,表示获得的硬币数。
输入样例
5 3
2 2 3 3
3 3 5 5
1 2 1 4
输出样例
14

思路:

这道题目很明显要用二维差分来做,二维差分其实和二维前缀和(见T4)类似,能快速的实现多个数据的改变。

代码如下:

#include<bits/stdc++.h> 
using namespace std;
int n,m,q,h,b[2010][2010];

void insert(int x1,int y1,int x2,int y2)
{
    
    
	b[x1][y1]+=1;
	b[x2+1][y1]-=1;
	b[x1][y2+1]-=1;
	b[x2+1][y2+1]+=1;
}    //二维差分函数
int main()
{
    
    
	ios::sync_with_stdio(false);
	cin>>n>>q;
	while(q--)
	{
    
    
		int x1,y1,x2,y2;
		cin>>x1>>y1>>x2>>y2;
		insert(x1,y1,x2,y2);//对(x1,y1)~(x2,y2)范围内的圈子套中次数加1
	}				
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			b[i][j]+=b[i-1][j]+b[i][j-1]-b[i-1][j-1];//前缀和
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			if(b[i][j]==1)
				h++;	 
	cout<<h;			
	return 0;
}

前缀和

T3 好奇的金鱼(二)

题目:

有n条金鱼排成一队,现在已知每条鱼的体重w。

好奇的金鱼会有m次询问,每次询问给出两个值(l,r)
(r >= l),想计算出[l,r]之间的金鱼体重之和。

输入
第一行2个数,n和m,中间用空格分隔
(1 <= n, m <= 100000)。
之后n+m行,
第 1 至 n 行:每行一个数字w(0 <= w <= 1000)
第 n + 1 至 n + m 行:每行2个数字l,r,中间用空格分隔
(0 < l <= r <= n)
输出
输出共m行,每行一个数,对应询问区间的金鱼体重之和。
输入样例
3 3
1
3
5
1 2
1 3
2 3
输出样例
4
9
8

思路:

其实我第一次做这道题的时候还是懵B一个,上来就直接循环m次找答案,然后…就TLE了,后来我认真学习,知道了前缀和这种方法,才A了这题。
前缀和:一种找l~r区间的和的方法,很快。开始每次都把从a1到当前的和找到(s[n]=s[n-1]+a[n]),求l到r区间的时候直接把s[r]-s[l-1] (不然会减掉l)就可以得到了。

代码如下:

#include<bits/stdc++.h> 
using namespace std;
int a[100010],s[100010],n,m,h,d,f;
int main()
{
    
    
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
    
    
		cin>>a[i];
		s[i]=a[i]+s[i-1]; //前缀和
	}
	for(int i=1;i<=m;i++)
	{
    
    
		cin>>d>>f;
		h=s[f]-s[d-1];
		cout<<h<<endl;
	}   
	return 0;
}

T4 子矩阵求和

题目:

给出一个n * m的矩阵a,矩阵元素a[i][j]小于1000,进行q次查询,每次查询给出子矩阵的4个边界(上下左右),求该子矩阵所有元素之和。

比如有一个查询:1 2 1 3 ,表示要计算从第1行到第2行,从第1列到第3列的所有元素之和。

在这里插入图片描述
输入
第一行2个整数n, m,中间用空格分割,分别对应数组的行数n、列数m(1 <= m,n <= 1000)

接下来n行,每行m个整数表示矩阵的内容a[i][j] 。
(0 <= a[i,j] <= 1000)

接下来1行,一个数q,对应查询的数量。(1 <= q <= 10000)

接下来q行,每行4个整数,对应子矩阵的上下左右边界u,d,l,r。(1 <= u <= d <= n, 1 <= l <= r <= m)
输出
共q行,对应q个询问的求和结果。

输入样例
3 4
1 2 3 4
5 6 7 8
9 10 11 12
3
1 3 1 2
1 2 1 3
1 3 1 3
输出样例
33
24
54

思路:

第一次看见这道题的时候,我刚学二维数组,强行用暴力解法(for循环)解,直接TLE,然后想起前缀和是个好东西,就用一维前缀和做了一下,完美的TLE了,想了半天,受到一维前缀和的启发,写出了二维前缀和,
顺利AC

代码如下:

#include<bits/stdc++.h> 
using namespace std;
int a[1010][1010],n,m,gg,u,d,l,r,s[1010][1010],h;
int main()
{
    
    
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
    
    
		for(int j=1;j<=m;j++)
		{
    
    
			cin>>a[i][j];
			s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+a[i][j];
		}
	}
	cin>>gg;
	for(int i=1;i<=gg;i++)
	{
    
    
		cin>>u>>d>>l>>r;
		cout<<s[d][r]-s[u-1][r]-s[d][l-1]+s[u-1][l-1]<<" ";
//二维前缀和
	}
	return 0;
}

差分前缀和系列——差分与前缀和1

猜你喜欢

转载自blog.csdn.net/SSL_wyd/article/details/113097397
今日推荐