Part 2.8 前缀和 & 差分

前缀和是一种重要的预处理,能大大降低查询的时间复杂度,而差分则是一种和前缀和相对的策略。
一般给定一个范围内的数据,求单个数据的时候用差分;给定单个数据,求范围数据时用前缀和。

[USACO16JAN] Subsequences Summing to Sevens S

题目描述

Farmer John’s N N N cows are standing in a row, as they have a tendency to do from time to time. Each cow is labeled with a distinct integer ID number so FJ can tell them apart. FJ would like to take a photo of a contiguous group of cows but, due to a traumatic childhood incident involving the numbers 1 … 6 1 \ldots 6 16, he only wants to take a picture of a group of cows if their IDs add up to a multiple of 7.

Please help FJ determine the size of the largest group he can photograph.

给你n个数,分别是a[1],a[2],…,a[n]。求一个最长的区间[x,y],使得区间中的数(a[x],a[x+1],a[x+2],…,a[y-1],a[y])的和能被7整除。输出区间长度。若没有符合要求的区间,输出0。

输入格式

The first line of input contains N N N ( 1 ≤ N ≤ 50 , 000 1 \leq N \leq 50,000 1N50,000). The next N N N

lines each contain the N N N integer IDs of the cows (all are in the range

0 … 1 , 000 , 000 0 \ldots 1,000,000 01,000,000).

输出格式

Please output the number of cows in the largest consecutive group whose IDs sum

to a multiple of 7. If no such group exists, output 0.

样例 #1

样例输入 #1

7
3
5
1
6
2
14
10

样例输出 #1

5

提示

In this example, 5+1+6+2+14 = 28.

代码实现

#include<iostream>
using namespace std;
#define MAX_N 50000
int yu[MAX_N+5];
int first[7],last[7];
int main()
{
	int n;
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>yu[i];
		yu[i]=(yu[i]+yu[i-1])%7;
	}
	for(int i=n;i>=1;i--)first[yu[i]]=i;
	first[0]=0;
	for(int i=1;i<=n;i++)last[yu[i]]=i;
	int ans=0;
	for(int i=0;i<7;i++)ans=max(last[i]-first[i],ans);
	cout<<ans;
	return 0;
}

最大正方形

题目描述

在一个 n × m n\times m n×m 的只包含 0 0 0 1 1 1 的矩阵里找出一个不包含 0 0 0 的最大正方形,输出边长。

输入格式

输入文件第一行为两个整数 n , m ( 1 ≤ n , m ≤ 100 ) n,m(1\leq n,m\leq 100) n,m(1n,m100),接下来 n n n 行,每行 m m m 个数字,用空格隔开, 0 0 0 1 1 1

输出格式

一个整数,最大正方形的边长。

样例 #1

样例输入 #1

4 4
0 1 1 1
1 1 1 0
0 1 1 0
1 1 0 1

样例输出 #1

2

代码实现

#include<iostream>
using namespace std;
#define MAX_N 100
int shang[MAX_N+5][MAX_N+5],zuo[MAX_N+5][MAX_N+5];
int dp[MAX_N+5][MAX_N+5];
int main()
{
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	for(int j=1;j<=m;j++)
	{
		cin>>dp[i][j];
		if(dp[i][j])
		{
			shang[i][j]=shang[i-1][j]+1;
			zuo[i][j]=zuo[i][j-1]+1;
		}	
	}
	int ans=0;
	for(int i=1;i<=n;i++)
	for(int j=1;j<=m;j++)
	{
		int x=dp[i-1][j-1];
		if(dp[i][j])
		{
			int temp=min(zuo[i][j-1],shang[i-1][j]);
			dp[i][j]=min(dp[i-1][j-1],temp)+1;
		}	
		ans=max(ans,dp[i][j]);
	}
	cout<<ans;
	return 0;
}

地毯

题目描述

n × n n\times n n×n 的格子上有 m m m 个地毯。

给出这些地毯的信息,问每个点被多少个地毯覆盖。

输入格式

第一行,两个正整数 n , m n,m n,m。意义如题所述。

接下来 m m m 行,每行两个坐标 ( x 1 , y 1 ) (x_1,y_1) (x1,y1) ( x 2 , y 2 ) (x_2,y_2) (x2,y2),代表一块地毯,左上角是 ( x 1 , y 1 ) (x_1,y_1) (x1,y1),右下角是 ( x 2 , y 2 ) (x_2,y_2) (x2,y2)

输出格式

输出 n n n 行,每行 n n n 个正整数。

i i i 行第 j j j 列的正整数表示 ( i , j ) (i,j) (i,j) 这个格子被多少个地毯覆盖。

样例 #1

样例输入 #1

5 3
2 2 3 3
3 3 5 5
1 2 1 4

样例输出 #1

0 1 1 1 0
0 1 1 0 0
0 1 2 1 1
0 0 1 1 1
0 0 1 1 1

提示

样例解释

覆盖第一个地毯后:

0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 1 1 1 1 1 1 0 0 0 0 0 0
0 0 0 1 1 1 1 1 1 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

覆盖第一、二个地毯后:

0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 1 1 1 1 1 1 0 0 0 0 0 0
0 0 0 1 1 1 2 2 2 1 1 1 1 1 1
0 0 0 0 0 0 1 1 1 1 1 1 1 1 1
0 0 0 0 0 0 1 1 1 1 1 1 1 1 1

覆盖所有地毯后:

0 0 0 1 1 1 1 1 1 1 1 1 0 0 0
0 0 0 1 1 1 1 1 1 0 0 0 0 0 0
0 0 0 1 1 1 2 2 2 1 1 1 1 1 1
0 0 0 0 0 0 1 1 1 1 1 1 1 1 1
0 0 0 0 0 0 1 1 1 1 1 1 1 1 1

数据范围

对于 20 % 20\% 20% 的数据,有 n ≤ 50 n\le 50 n50 m ≤ 100 m\le 100 m100

对于 100 % 100\% 100% 的数据,有 n , m ≤ 1000 n,m\le 1000 n,m1000

代码实现

#include<iostream>
using namespace std;
#define MAX_N 1000
int ans[MAX_N+5][MAX_N+5];
int cha[MAX_N+5][MAX_N+5];
int main()
{
	int n,m;
	cin>>n>>m;
	int a,b,c,d;
	for(int i=1;i<=m;i++)
	{
		cin>>a>>b>>c>>d;
		for(int j=a;j<=c;j++)cha[j][b]+=1;
		if(d!=n)for(int j=a;j<=c;j++)cha[j][d+1]-=1;
		else for(int j=a;j<=c;j++)cha[j+1][1]-=1;
	}
	int x=0;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=n;j++)
		{
			x+=cha[i][j];
			if(j!=1)cout<<" ";
			cout<<x;
		}
		cout<<endl;
	}
	return 0;
}

[HNOI2003] 激光炸弹

题目描述

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

现在你的任务是计算一颗炸弹最多能炸掉地图上总价值为多少的目标。

可能存在多个目标在同一位置上的情况。

输入格式

输入的第一行为整数 n n n 和整数 m m m

接下来的 n n n 行,每行有 3 3 3 个整数 x , y , v x, y, v x,y,v,表示一个目标的坐标与价值。

输出格式

输出仅有一个正整数,表示一颗炸弹最多能炸掉地图上总价值为多少的目标(结果不会超过 32767 32767 32767 )。

样例 #1

样例输入 #1

2 1
0 0 1
1 1 1

样例输出 #1

1

提示

数据规模与约定

  • 对于 100 % 100\% 100% 的数据,保证 1 ≤ n ≤ 1 0 4 1 \le n \le 10^4 1n104 0 ≤ x i , y i ≤ 5 × 1 0 3 0 \le x_i ,y_i \le 5\times 10^3 0xi,yi5×103 1 ≤ m ≤ 5 × 1 0 3 1 \le m \le 5\times 10^3 1m5×103 1 ≤ v i < 100 1 \le v_i < 100 1vi<100

代码实现

#include<iostream>
#include<algorithm>
using namespace std;
#define MAX_N 10000
#define MAX_X 5000
struct Data{
	int x,y,v;
}target[MAX_N+5];
int he[MAX_X+5][MAX_X+5];
bool cmp(Data&a,Data&b)
{
	if(a.x!=b.x)return a.x<b.x;
	return a.y<b.y;
}
int main()
{
	int n,m;
	cin>>n>>m;
	int max_x=0,max_y=0;
	for(int i=1;i<=n;i++)
	{
		cin>>target[i].x>>target[i].y>>target[i].v;
		target[i].x+=1;
		target[i].y+=1;
		max_x=max(max_x,target[i].x);
		max_y=max(max_y,target[i].y);
	}
	sort(target+1,target+1+n,cmp);
	int cnt=1,flag=0;
	for(int i=1;i<=max_x;i++)
	{
		for(int j=1;j<=max_y;j++)
		{
			he[i][j]=he[i-1][j]+he[i][j-1]-he[i-1][j-1];
			while(cnt<=n&&target[cnt].x==i&&target[cnt].y==j)
			{
				he[i][j]+=target[cnt].v;
				cnt++;
			}
		}
		if(flag)break;
	}
	int ans=0;
	int range_x=min(m,max_x),range_y=min(m,max_y);
	for(int i=range_x;i<=max_x;i++)
	{
		for(int j=range_y;j<=max_y;j++)
		{
			int temp=he[i][j]-he[i-range_x][j]-he[i][j-range_y]+he[i-range_x][j-range_y];
			ans=max(ans,temp);
		}
	}
	cout<<ans;
	return 0;
}

[Poetize6] IncDec Sequence

题目描述

给定一个长度为 n n n 的数列 a 1 , a 2 , ⋯   , a n {a_1,a_2,\cdots,a_n} a1,a2,,an,每次可以选择一个区间 [ l , r ] [l,r] [l,r],使这个区间内的数都加 1 1 1 或者都减 1 1 1

请问至少需要多少次操作才能使数列中的所有数都一样,并求出在保证最少次数的前提下,最终得到的数列有多少种。

输入格式

第一行一个正整数 n n n
接下来 n n n 行,每行一个整数,第 $i+1 $行的整数表示 a i a_i ai

输出格式

第一行输出最少操作次数
第二行输出最终能得到多少种结果

样例 #1

样例输入 #1

4
1
1
2
2

样例输出 #1

1
2

提示

对于 100 % 100\% 100% 的数据, n ≤ 100000 , 0 ≤ a i ≤ 2 31 n\le 100000, 0 \le a_i \le 2^{31} n100000,0ai231

代码实现

#include<iostream>
using namespace std;
#define MAX_N 100000
long long a[MAX_N+5],cha[MAX_N+5];
int main()
{
	int n;
	cin>>n;
	long long zheng=0,fu=0;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
		if(i==1)continue;
		cha[i]=a[i]-a[i-1];
		if(cha[i]>0)zheng+=cha[i];
		else if(cha[i]<0)fu-=cha[i];
	}
	long long ans1=max(zheng,fu);
	long long ans2=max(zheng,fu)-min(zheng,fu)+1;
	cout<<ans1<<endl<<ans2;
	return 0;
}
  • 29
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值