蓝桥杯专项复习——前缀和和差分

目录

基础知识

一维前缀和

【模板】前缀和

[Tokitsukaze and Average of Substring](#Tokitsukaze and Average of Substring)

二维前缀和(在考场上一般是现推)

求二维前缀和公式(求增加点(i,j)时虚线上方的区域):

求一个区域内的值(红色区域),需要知道两个点,减去图上虚线部分,再将多减去的加回来即可(+1是为了包含边缘点,可类比一维前缀和):

【模板】二维前缀和

​编辑​编辑​编辑

[HNOI2003]激光炸弹

一维差分

【模版题】差分

值周

排列计算

基础知识

一维前缀和

【模板】前缀和

复制代码
#include<bits/stdc++.h>
#define int long long 
using namespace std;

const int N=100000+10;
int n,q;
int a[N],s[N];

signed main()
{
	cin>>n>>q;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
		s[i]=s[i-1]+a[i];
	}
	while(q--)
	{
		int l,r;cin>>l>>r;
		cout<<s[r]-s[l-1]<<endl;
	}
	return 0;
 } 

Tokitsukaze and Average of Substring

详解:点击跳转

时间复杂度分析:n^2<1e7,1s大概是1e8,双层遍历不会超时

思路:分别预处理每个字符的前缀和,才可以用O(1)求出每个l-r中相同字符的对数

tips:

对于cnt,是要将所有数对相加,已知数对之间存在规律,即之间为一个等差数列,所有利用高斯求和公式(1+2+3+......+n=n*(n-1)/2)求出每一组l和r对应的所有数对

复制代码
#include<bits/stdc++.h>
#define int long long

using namespace std;

const int N=5000+10;

int pre[30][N];//前缀和数组
 
void solve()
{
	int n;cin>>n;
	string s;cin>>s;
	
	for(int i=1;i<=n;i++)//遍历每一个字符 
	{
		//将每一个字符转换成数字,如将a映射成1、b映射成2......
		int cnt=s[i-1]-'a'+1;
		for(int j=1;j<=26;j++)//使用前缀和进行预处理 
		{
			if(j==cnt)
				pre[j][i]=pre[j][i-1]+1;
			else
				pre[j][i]=pre[j][i-1];
		 } 
	}
	
	double ans=0.0;//初始化
	for(int i=1;i<=n;i++)//对于每一个字符对进行遍历 
	{
		for(int j=i+1;j<=n;j++)//对第i个字符的后面字符进行遍历 
		{
			int l=i,r=j;//左右区间 
			double cnt=0;//cnt是每一个C函数的值,即一个区间的数量
			//枚举26个字符l和r之间的数对数,在前面的预处理时已经过滤掉不相关的字符 
			for(int k=1;k<=26;k++)
			{
				int tmp=pre[k][r]-pre[k][l-1];//l~r区间之间的对数 
				//左边是对不同字符间的情况相加,右边是对每一个相同的字符进行求和 
				cnt+=tmp*(tmp-1)/2; 
			}
			ans=max(ans,cnt/(double)(r-l+1));//取 f函数最值 
		}
	 } 
	printf("%.6f\n",ans);
	
	//由于有多组测试数据,还要将pre数组置0
	for(int i=1;i<=26;i++)
		for(int j=1;j<=n;j++) 
			pre[i][j]=0;
 } 
signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	
	int t;
	cin>>t;
	while(t--)//将每一个数据点用一个函数解决 
		solve();
	return 0;
}

二维前缀和(在考场上一般是现推)

求二维前缀和公式(求增加点(i,j)时虚线上方的区域):

求一个区域内的值(红色区域),需要知道两个点,减去图上虚线部分,再将多减去的加回来即可(+1是为了包含边缘点,可类比一维前缀和):

【模板】二维前缀和

复制代码
#include<bits/stdc++.h>
#define int long long 
using namespace std;

const int N=1000+10;

int n,m,q;
int pre[N][N],g[N][N];
int ans;

signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	
	cin>>n>>m>>q;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			cin>>g[i][j];
			//预处理前缀和 
			pre[i][j]=pre[i][j-1]+pre[i-1][j]-pre[i-1][j-1]+g[i][j];
		}
	}
	while(q--)
	{
		int x1,y1,x2,y2;
		cin>>x1>>y1>>x2>>y2;
		ans=pre[x2][y2]-pre[x2][y1-1]-pre[x1-1][y2]+pre[x1-1][y1-1];
		cout<<ans<<endl;
	}
	
	return 0;
 } 

[HNOI2003]激光炸弹

抽象出来就是要求求一个矩形内的一个正方形区域内的最大的值的和。

需要枚举矩形中正方形能圈住的每一个区域,前面预处理将5001所有都枚举了,后面再将所有的r正方形枚举一定不会错。

注意!!!一个方格能有多个目标,所以需要写成g+=而不是g=

复制代码
#include<bits/stdc++.h>
#define int long long

using namespace std;

const int N=5000+10;

int g[N][N],pre[N][N];//g是单独的一个小正方形的值,pre是前缀和 
int ans; 
signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	
	int n,r;
	cin>>n>>r;
	
	for(int i=1;i<=n;i++)
	{
		int x,y,v;cin>>x>>y>>v;
		//由于坐标是从0开始,为了方便二位前缀和,将总体++使其从1开始,预处理时需要加1
		x++;y++;
		//一个方格能有多个目标,所以需要写成g+=而不是g= 
		g[x][y]+=v;
	}
	
	for(int i=1;i<=5001;i++)
		for(int j=1;j<=5001;j++)//前缀和预处理 
		{
			pre[i][j]=pre[i-1][j]+pre[i][j-1]-pre[i-1][j-1]+g[i][j];
		}
		
	for(int i=r;i<=5001;i++)//前面的点从1开始,所有需要遍历到5001 
	{
		for(int j=r;j<=5001;j++)
		{
			int x1=i-r+1,y1=j-r+1;
			int x2=i,y2=j;//x1从1开始,x1与x2是一个对角线关系
			int tmp=pre[x2][y2]-pre[x2][y1-1]-pre[x1-1][y2]+pre[x1-1][y1-1];
			ans=max(ans,tmp);
		}
	}
	cout<<ans<<endl;
	return 0;
}

一维差分

【模版题】差分

输入样例:

复制代码
6 3
1 2 2 1 2 1
1 3 1
3 5 1
1 6 1

输出样例:

复制代码
3 4 5 3 4 2

#include<bits/stdc++.h>
#define int long long 
using namespace std;

const int N=100000+10;

int b[N],a[N];

signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);

	int n,m;
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
		b[i]=a[i]-a[i-1];//构建差分数组 
	}
	
	while(m--)//具有累加效果,一起写能降低时间复杂度 
	{
		int l,r,c;
		cin>>l>>r>>c;
		b[l]+=c;b[r+1]-=c;//通过差分实现a数组每个都加上c 
	}		
	for(int i=1;i<=n;i++)
	{
		b[i]+=b[i-1];//将差分相加,相当于a数组
		cout<<b[i]<<' ';
	}
	cout<<endl;
	
	return 0;
 } 

值周

输入样例

复制代码
500 3
150 300
100 200
470 471

输出样例

复制代码
298

说明

复制代码
对于所有的数据,1≤L≤100000000

对于10%的数据,1<=M<=100

对于20%的数据,1<=M<=1000

对于50%的数据,1<=M<=100000

对于100%的数据,1<=M<=1000000

思路:

是一个明显的区间操作,使用差分

即在l和r范围内减去1

注意:

判断答案时,不能写if(b[i]),因为由题意知区间可能叠加,而且利用差分进行多次减法时是有累加的效果,b[i]可能是负数,这时会使答案变多

复制代码
#include<bits/stdc++.h>
#define int long long 

using namespace std;

const int N=100000000+10;

int a[N],b[N];

signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	
	int n,m;
	cin>>n>>m;
	for(int i=0;i<=n;i++)
		a[i]=1;
	
	b[0]=1;//需要将b[0]赋初值,因为for循环中i-1会越界 
	for(int i=1;i<=n;i++)
	{
		b[i]=a[i]-a[i-1];//差分数组 
	}
	while(m--)
	{
		int l,r;cin>>l>>r;
		b[l]-=1;b[r+1]+=1;
	}
	int ans=0;
	for(int i=0;i<=n;i++)
	{
		b[i]+=b[i-1];
		//不能写if(b[i]),因为由题意知区间可能叠加
		//而且利用差分进行多次减法时是有累加的效果,b[i]可能是负数,这时会使答案变多
		if(b[i]>0)
			ans++;
	}
	cout<<ans<<endl;
	return 0;
}

排列计算

输入样例

复制代码
7 3
1 3
3 7
5 6

输出样例

复制代码
46

思路:

有1到n个数字,然后m个查询,构造一个序列,使得查询后的值最大化。

因为只输出一次,不需要关心到底是怎么构造的,考虑,被查询到的数字次数越多,那么就让他的值越大,则,差分前缀和求出每个数字被查询的次数,然后排序,出现次数最小的对应1,最大的对应n即可。

复制代码
#include<bits/stdc++.h>
#define int long long 
using namespace std;

const int N=100000+10;

int b[N];

signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	
	int n,m;//n个数,m步操作 
	cin>>n>>m;
	
	while(m--)//直接构建拆分函数,记录每个数字被查询的次数 
	{
		//即实现l~r区间的数都加1 
		int l,r;cin>>l>>r;
		b[l]+=1;b[r+1]-=1; 
	}
	
	for(int i=1;i<=n;i++)
		b[i]+=b[i-1];//每个数字被查询的次数
		
	sort(b+1,b+1+n);//将数组内容进行升序排序,方便后面的计算
	int ans=0;
	for(int i=1;i<=n;i++)
	{
		//这里i是1~n的排序中的数,而b[i]是每个数被查询的次数,所以要用乘法 
		ans+=i*b[i];
	}
	cout<<ans<<endl;
	
	return 0; 
}
相关推荐
Fantasydg2 小时前
DAY 38 leetcode 15--哈希表.三数之和
算法·leetcode·散列表
编程绿豆侠2 小时前
力扣HOT100之链表:19. 删除链表的倒数第 N 个结点
算法·leetcode·链表
ゞ 正在缓冲99%…2 小时前
leetcode274.H指数
java·算法·leetcode
liulun3 小时前
Windows注册鼠标钩子,获取用户选中的文本
c++·windows·qt
柃歌5 小时前
【LeetCode Solutions】LeetCode 136 ~ 140 题解
数据结构·算法·leetcode
杰瑞学AI5 小时前
LeetCode详解之如何一步步优化到最佳解法:21. 合并两个有序链表
数据结构·python·算法·leetcode·链表·面试·职场和发展
石去皿5 小时前
力扣hot100 71-80记录
算法·leetcode·职场和发展
佚明zj5 小时前
[ISP] raw图常见的噪声种类以及生成原因
算法
无 证明5 小时前
【C++】类和对象 (第一弹)
开发语言·c++·算法
xianduan_6 小时前
leetcode刷题记录44-208. 实现 Trie (前缀树)
算法·leetcode·翻译