码迷,mamicode.com
首页 > 其他好文 > 详细

团队训练(七)

时间:2020-06-05 00:43:53      阅读:57      评论:0      收藏:0      [点我收藏+]

标签:两个指针   排列   cto   code   这一   动态数组   灵活运用   空格   ems   

团队训练(七)-- 前缀和基础

前言:感觉遇到一些题可以结合前缀和使用,降低时间复杂度,就找了比较基础的题目来练练,前三题基本就是概念,第四题结合了上次的二分,相对还是比较熟练,第五题加了点数学思维,卡了一下,重点是第五题读题懵逼了,第六题用到差分,知识点全部都见过,这次的题目相对找的比较容易吧。

1.前缀和
题目

小明是一个好学的孩子,一天他学会了编程,在解题,题目是这样的:

题目描述
有 N 个的正整数放到数组 A 里,现在要求你求出并输出这样一个数组:

这个数组的第 i 个数是数组 A 的第一个到数组 A 的第 i 个数的和。

输入格式
第一行 1 个正整数:N,N 范围在[1,100]。 第二行 N 个正整数:范围在[1,10000]。

输出格式
N 个正整数。

输入输出样例
输入
6
2 6 1 9 7 3

输出
2 8 9 18 25 28
说明/提示
你能帮小明解决这道蒟蒻题吗?

数据已经调至最水,只需在思路上耕耘,不需考虑数据

解析:就是一个前缀和加起来,然后其实可以用一个数组就能解决,s[i] += s[i-1]即可,不过为了更加明了多开了一个。

AC代码:

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<stdlib.h>
#include<string.h>
#include<math.h>
using namespace std;
typedef long long LL;
const int maxn = 1e4 + 7;
const int maxm = 2e5 + 7;
const long long inf = 0x3f3f3f3f;
const long long mod = 1e9 + 7;
int a[maxn], s[maxn];
int n;
int main()
{
	cin >> n;
	for(int i = 1; i <= n; i++)
	{
		cin >> a[i];
		s[i] += s[i-1] + a[i];
	}
	for(int i = 1; i <= n; i++)
		cout << s[i] << " ";
	return 0;
}

2.前缀和的逆
题目

于是按照套路,明明刷前缀和的题目刷累了,他瞅了瞅这道题,觉得很简单,就想请让你来帮忙做这一题:

有 N 个正整数放到数组B 里,它是数组 A 的前缀和数组,求 A 数组。

输入格式
第一行 1 个正整数 N。 第二行 N 个正整数。

输出格式
N 个正整数。

输入输出样例
输入
6
2 10 20 25 30 43

输出
2 8 10 5 5 13

说明/提示
对于 100% 的数据,满足 N≤100、Bi ≤10000。

解析:前缀和的逆???差分不求和就完事。

AC代码:

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<stdlib.h>
#include<string.h>
#include<math.h>
using namespace std;
typedef long long LL;
const int maxn = 1e3 + 7;
const int maxm = 2e5 + 7;
const long long inf = 0x3f3f3f3f;
const long long mod = 1e9 + 7;
int a[maxn], d[maxn];
int n;
int main()
{
	cin >> n;
	for(int i = 1; i <= n; i++)
	{
		cin >> a[i];
		d[i] =  a[i] - a[i-1];
	}
	for(int i = 1; i <= n; i++)
		cout << d[i] << " ";
	return 0;
}

3.最大の和
题目

读入n个整数的数列a1,a2,…,an和正整数k(1<=k<=n),请输出连续排列的k个整数的和的最大值

输入
第一行是正整数n(1<=n<=100000)和正整数k(1<=k<=n) 第二行以后的第1+i(1<=i<=n)至最后一行为数列

输出
仅一行,仅包括最大值(最后需要换行)。

样例输入
5 3
2 5 -4 10 3

样例输出
11

解析:因为要找k个连续的数,直接拿两个指针i,j夹住区间同时移动就可以,然后存一下最大值。

AC代码:

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<stdlib.h>
#include<string.h>
#include<math.h>
using namespace std;
typedef long long LL;
const int maxn = 1e5 + 7;
const int maxm = 2e5 + 7;
const long long mod = 1e9 + 7;
const long long inf = 0x3f3f3f3f;
LL maxSum = -inf, sum;
LL a[maxn], s[maxn];
int n, k;

int main()
{
	cin >> n >> k;
	for(int i = 1; i <= n; i++)
	{
		cin >> a[i];
		s[i] = s[i-1] + a[i];
	}
	for(int i = 0, j = k; j <= n; i ++, j ++)
	{
		sum = s[j] - s[i];
		if(sum > maxSum)
			maxSum = sum;
	}
	cout << maxSum << endl; 
}

4.连续自然数和
题目

对一个给定的自然数M,求出所有的连续的自然数段,这些连续的自然数段中的全部数之和为M。

例子:1998+1999+2000+2001+2002=10000,所以从1998到2002的一个自然数段为M=10000的一个解。

输入格式
包含一个整数的单独一行给出M的值(10≤M≤2,000,000)。

输出格式
每行两个自然数,给出一个满足条件的连续自然数段中的第一个数和最后一个数,两数之间用一个空格隔开,所有输出行的第一个按从小到大的升序排列,对于给定的输入数据,保证至少有一个解。

输入
10000

输出
18 142
297 328
388 412
1998 2002

解析:二分 + 前缀和。
题目给了M的值,因为是连续自然序段,说明至少有两个数,那其中最大的数最多为M/2 + 1,加1是为了取整,接着就是固定一个左区间,去二分找一个右区间满足它们之和为M即可,这里就要借助一下前缀和来计算枚举的[l,r]之和。

AC代码:

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<stdlib.h>
#include<string.h>
#include<math.h>
using namespace std;
typedef long long LL;
const int maxn = 1e5 + 7;
const int maxm = 2e6 + 5;
const long long inf = 0x3f3f3f3f;
const long long mod = 1e9 + 7;
LL s[maxm];
int m;

int check(int x, int tempL)
{
	if(s[x] - s[tempL] >= m)
		return 1;
	else
		return 0;
}

int main()
{
	cin >> m;
	for(int i = 1; i <= m; i++)
		s[i] = i + s[i-1];
	for(int i = 0; i <= m; i ++)
	{
		LL l = 0, r = (m / 2) + 1;
		while(l < r)
		{
			int mid = l + r >> 1;
			if(check(mid, i))
				r = mid;
			else
				l = mid + 1;
		}
		if(s[r] - s[i] == m)
			printf("%d %d\n", i+1, r);
	}
	return 0;
} 

/*
1532139

36459 36500
72949 72969
109432 109445
218874 218880
255354 255359
510712 510714
766069 766070


*/


5.珂朵莉与宇宙
题目

星神是来自宇宙的
所以珂朵莉也是吧
所以我就出了个题
给你一个长为n的序列a,有n*(n+1)/2个子区间,问这些子区间里面和为完全平方数的子区间个数

输入描述:
第一行一个数n
第二行n个数表示序列a
输出描述:
输出一个数表示答案

输入
6
0 1 0 9 1 0

输出
11

备注:
1 <= n <= 100000
0 <= ai <= 10

解析:前缀和 + 枚举 + 数学思维
吐槽一波这个题意的子区间,一开始真的懵,感觉说是子段可能好理解一些吧,不过这里的子区间是这样理解,若给一个长度为2的子序列为1,4。那子区间便是(1),(4),(1,4)。假如是给一个长度为3的序列为1,5,2。那子区间便是(1),(5),(2),(1,5),(5,2),(1,5,2)。再多写一个,那长度为4的序列为2,3,0,1。那子区间则为(2),(3),(0),(1),(2,3),(3,0),(0,1),(2,3,0)(3,0,1),(2,3,0,1)。说个连续子段和它不香嘛?
题意理解后就是找子区间个数,假如直接无脑枚举时间复杂度肯定是O(n^2),直接T掉,那假如计算这些子区间的前缀和sum[i],可以知道其实就是找sum[i] - sum[x] = 某个完全平方数(s2),但假如这里直接去判断他们的前缀和之差(其实就是一段子区间的和)是不是一个完全平方数还是得O(n2),不过可以换个思路,将式子变成sum[i] - s^2 = sum[x],枚举完全平方数,再看看sum[x],是不是存在就可以了。
因为ai最大是10,n最大为1e5,前缀和最大也就是1e6,那s最大也就是1e3,整个程序的时间复杂度为O(n * 1000),勉强可以过。还有就是注意一下,sum[0]初始必须是1,因为数组下标为0的前缀和就是0,并且肯定存在,然后算完一个sum[i],就让它在cnt[sum[i]]里面++,说明sum[i]的存在,以便下一次找sum[x],因为这里sum[i]一定是大于sun[x]的,所以算完一次sum[i]满足的完全平方数再记录它的存在没毛病。cnt这个数组其实就是记录sum[x]是否存在,假如不存在cnt[sum[x]]的个数一定是为0的。

AC代码:

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<stdlib.h>
#include<string.h>
#include<math.h>
using namespace std;
typedef long long LL;
const int maxn = 1e5 + 7;
const int maxm = 1e6 + 7;
const long long inf = 0x3f3f3f3f;
const long long mod = 1e9 + 7;
int cnt[maxm], sum[maxn];
LL num;
int n;
int main()
{
	cin >> n;
	for(int i = 1; i <= n; i++)
	{
		cin >> sum[i];
		sum[i] += sum[i-1];
	}
	cnt[0] = 1; //sum[0]的个数为1 
	for(int i = 1; i <= n; i++)
	{
		for(int s = 0; s * s <= sum[i]; s++)
			num += cnt[sum[i] - s * s];
			cnt[sum[i]] ++;  
	}
	cout << num;
	return 0;
}

6.最高的牛
题目

有 N 头牛站成一行,被编队为1、2、3…N,每头牛的身高都为整数。
当且仅当两头牛中间的牛身高都比它们矮时,两头牛方可看到对方。
现在,我们只知道其中最高的牛是第 P 头,它的身高是 H ,剩余牛的身高未知。
但是,我们还知道这群牛之中存在着 M 对关系,每对关系都指明了某两头牛 A 和 B 可以相互看见。
求每头牛的身高的最大可能值是多少。
输入格式
第一行输入整数N,P,H,M,数据用空格隔开。
接下来M行,每行输出两个整数 A 和 B ,代表牛 A 和牛 B 可以相互看见,数据用空格隔开。

输出格式
一共输出 N 行数据,每行输出一个整数。
第 i 行输出的整数代表第 i 头牛可能的最大身高。
数据范围
1≤N≤10000,
1≤H≤1000000,
1≤A,B≤10000,
0≤M≤10000

输入样例:
9 3 5 5
1 3
5 3
4 3
3 7
9 8

输出样例:
5
4
5
3
4
4
5
5
5

注意:
此题中给出的关系对可能存在重复

解析:前缀和 + 差分。
这道题问的是每头牛最高的高度可能是多少,并且已经给出最高的牛的高度,一开始我们可以假设所有的牛都是这个高度,假如编号为1的牛可以看到4的牛,那编号为2和3的牛高度至少要比1和4少1,根据这个思想就可以差分了,再注意一下就是关系可能存在重复,再者就是每对关系不一定是l < r。
起初写的代码时间复杂度到O(m^2)数量级,居然还过了,不过当时算还是处于e8数量级,可能评测机强大???还是数据水了,不太清楚,但写的这么大时间复杂度原因是没能灵活运用差分的性质,其实差分这个数组可以最后再求前缀和,但我们没算一组关系都求了一次,再者就是不了解swap函数,还是map容器,map相当于一个动态数组,可以标记给出的关系是否重复,这样显得代码更加简洁。

无脑代码:

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<stdlib.h>
#include<string.h>
#include<math.h>
using namespace std;
typedef long long LL;
const int maxn = 1e4 + 7;
const int maxm = 1e6 + 7;
const long long inf = 0x3f3f3f3f;
const long long mod = 1e9 + 7;
int n, p, h, m, r, l;
LL a[maxn], delt[maxn];
struct node
{
	int x1, x2;
}pos[maxn];

int main()
{
	cin >> n >> p >> h >> m;
	for(int i = 1; i <= m; i++)
	{
		cin >> pos[i].x1 >> pos[i].x2;
		for(int j = 1; j < i; j++ )
			if((pos[i].x1 == pos[j].x1 && pos[i].x2 == pos[j].x2) || (pos[i].x2 == pos[j].x1 && pos[i].x1 == pos[j].x2))
				pos[i].x1 = 1, pos[i].x2 = 2;
		if(pos[i].x1 > pos[i].x2)
		{
			r = pos[i].x1;
			l = pos[i].x2;
		}
		else
		{
			r = pos[i].x2;
			l = pos[i].x1;
		}
		if(r - l != 1)
		{
			memset(delt, 0, sizeof(delt));
			// 利用差分来将[l,r]区间里除l,r外的牛高度减1 
			for(int i = l; i <= r; i++)
			{
				if(i == l && a[i-1] != 0)
					delt[i] = a[i];
				else
					delt[i] = a[i] - a[i-1];
			}	
			delt[l+1]--;
			delt[r]++;
			for(int i = l; i <= r; i++)
			{
				delt[i] += delt[i-1];
				a[i] = delt[i];
			}
		}
	}
	for(int i = 1; i <= n; i++)
			cout << a[i] + h << endl;
	return 0;
}
 

优化代码:

#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string>
#include<vector>
#include<map>
#include<stdlib.h>
#include<string.h>
#include<math.h>
using namespace std;
typedef long long LL;
const int maxn = 1e4 + 7;
const int maxm = 1e6 + 7;
const long long inf = 0x3f3f3f3f;
const long long mod = 1e9 + 7;
int n, p, h, m, r, l;
LL delt[maxn];
map<int,int>a[maxn];

int main()
{
	cin >> n >> p >> h >> m;
	for(int i = 1; i <= m; i++)
	{
		cin >> l >> r;
		if(l > r)
			swap(l, r);
		if(a[l][r])
			continue;
		else
			a[l][r] = 1;
		delt[l+1] --;
		delt[r] ++;	
	}
	for(int i = 1; i <= n; i++)
	{
		delt[i] += delt[i-1];
		cout << delt[i] + h << endl;
	}
	return 0;
}
 

团队训练(七)

标签:两个指针   排列   cto   code   这一   动态数组   灵活运用   空格   ems   

原文地址:https://www.cnblogs.com/K2MnO4/p/13047270.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!