标签:两个指针 排列 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