题目1:k倍区间
给定一个长度为 N 的数列,A1,A2,…AN,如果其中一段连续的子序列 Ai,Ai+1,…Aj 之和是 K 的倍数,我们就称这个区间 [i,j] 是 K 倍区间。
你能求出数列中总共有多少个 K 倍区间吗?
输入格式
第一行包含两个整数 N 和 K。
以下 N 行每行包含一个整数 Ai。
输出格式
输出一个整数,代表 K 倍区间的数目。
数据范围
1≤N,K≤100000,
1≤Ai≤100000
输入样例:
5 2
1
2
3
4
5
输出样例:
6
解题思路:
题目要求求所给区间的子区间和,可以暴力枚举所有子区间,然后利用前缀和求区间和。枚举子区间一般有两种方式:枚举区间的两端点,或者枚举区间的某端点和区间的长度。这两种方式都需要两重循环,时间复杂度为O(n2)根据题目所给数据范围就超出了。可以在此基础上对代码进行优化。
其中两重循环的代码为
for(int r = 1; r <= n; i++)
for(int l = 1; l <= r; l++)
if((s[r] - s[l-1]) % k == 0) res ++;
可以发现,我们在内层循环中实际上找的是在0~r-1中有多少个s[i]与s[r]的余数相同,这样就又可以用桶进行优化。
优化后的代码为:
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 100010;
typedef long long LL;
int n, k;
LL s[N];
int cnt[N];
int main()
{
cin >> n >> k;
for(int i = 1; i <= n; i++)
{
scanf("%lld", &s[i]);
s[i] += s[i-1];
}
LL res = 0;
for(int i = 0; i <= n; i++)
{
res += cnt[s[i] % k];
cnt[s[i] % k] ++;
}
cout << res << endl;
return 0;
}
桶数组的妙用
问题:给定一个n个数的序列和一个整数k,求在第一个数到第i-1个中有多少个数与第i个数除以k的余数相同。
利用桶数组可以把时间复杂度降到O(n)
for(int i = 0; i <= n; i++)
{
res += cnt[a[i] % k]; //cnt[x]表示在a[0]到a[i-1]中余数为x的数有多少个
cnt[a[i] % k] ++;
}
题目2:递增三元组
给定三个整数数组
A=[A1,A2,…AN],
B=[B1,B2,…BN],
C=[C1,C2,…CN],
请你统计有多少个三元组 (i,j,k) 满足:
1≤i,j,k≤N
Ai<Bj<Ck
输入格式
第一行包含一个整数 N。
第二行包含 N 个整数 A1,A2,…AN。
第三行包含 N 个整数 B1,B2,…BN。
第四行包含 N 个整数 C1,C2,…CN。
输出格式
一个整数表示答案。
数据范围
1≤N≤105,
0≤Ai,Bi,Ci≤105
输入样例:
3
1 1 1
2 2 2
3 3 3
输出样例:
27
解题思路:
首先会想到暴力枚举,三重循环枚举所有情况,发现数据范围太大会超时
想优化,根据数据范围可以接受的时间复杂度为O(n)、O(nlogn)也就是只能枚举一个数组,由于A, B, C要求有序,所以枚举B数组,然后对于枚举每一个B看有多少个符合要求的A, C最终结果为A*C。
求一个数组中大于某个数的元素个数:前缀和、sort+二分、双指针
前缀和做法:
用桶数组记录每个元素出现的次数,然后对求桶数组的前缀和s[i]就表示数组中0~i的元素出现的次数。
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 1e5 + 10;
int n;
int as[N], cs[N];
int a[N], b[N], c[N];
int main()
{
cin >> n;
for(int i = 0; i < n; i++) scanf("%d", &a[i]), a[i]++;
for(int i = 0; i < n; i++) scanf("%d", &b[i]), b[i]++;
for(int i = 0; i < n; i++) scanf("%d", &c[i]), c[i]++;
for(int i = 0; i < n; i++) as[a[i]]++;
for(int i = 0; i < n; i++) cs[c[i]]++;
for(int i = 1; i < N; i++)
{
as[i] += as[i-1];
cs[i] += cs[i-1];
}
long long res = 0;
for(int i = 0; i < n; i++)
{
long long sum1, sum2;
sum1 = as[b[i]-1];
sum2 = cs[N-1] - cs[b[i]];
res += sum1 * sum2;
}
cout << res << endl;
return 0;
}