为了更好的阅读体检,可以查看我的算法学习网
在线评测链接:P1050
题面
给定 n n n个整数 a 1 , . . . , a n a_1,...,a_n a1,...,an和一个整数 x x x。求有多少有序对 ( i , j (i,j (i,j)满足 a i − a j = x a_i-a_j= x ai−aj=x。
输入格式
第一行两个整数 n ( 1 ≤ n ≤ 2 × 1 0 6 ) , x ( − 2 × 1 0 6 ≤ x ≤ 2 × 1 0 6 ) n(1 \leq n \leq 2 \times 10^6),x(-2 \times 10^6 \leq x \leq 2 \times 10^6) n(1≤n≤2×106),x(−2×106≤x≤2×106),分别代表整数的个数和题目中的 x x x。
第二行 n n n个用空格隔开的整数,第 i i i个代表 − 2 × 1 0 6 ≤ a i ≤ 2 × 1 0 6 -2 \times 10^6 \leq a_i \leq 2 \times 10^6 −2×106≤ai≤2×106
输出格式
一行一个整数,代表满足 a i − a j = x a_i-a_j=x ai−aj=x的有序对 ( i , j ) (i,j) (i,j)个数。
样例
input
5 1
1 1 5 4 2
ouput
3
提示
( i , j ) (i,j) (i,j) 为 ( 5 , 1 ) , ( 5 , 2 ) , ( 3 , 4 ) (5,1),(5,2),(3,4) (5,1),(5,2),(3,4)
思路
1.双指针暴力法
双重循环枚举 i , j i,j i,j 来计数即可,复杂度是 O ( n 2 ) O(n^2) O(n2)。但是无法拿到满分。 服务器一般一秒跑 1 e 8 1e8 1e8 次。
把 n n n 带进去看看 ( 2 ∗ 1 0 6 ) 2 = 4 ∗ 1 0 12 > > 1 e 8 (2 * 10^6)^2=4 * 10^{12} >> 1e8 (2∗106)2=4∗1012>>1e8
2.桶预处理法
先将所有数装进桶中。扫一遍数组枚举每一个
a
i
a_i
ai , 那么此时已知的数(常数)为
a
i
,
x
a_i,x
ai,x
①
a
i
−
a
j
=
x
②
a
j
=
a
i
−
x
①a_i-a_j=x \\ \\ ②a_j=a_i-x
①ai−aj=x②aj=ai−x
所以我们的任务就是在整个序列中寻找有多少个
a
j
a_j
aj 满足等式②。由于我们已经预处理了桶。所以直接查询
a
i
−
x
a_i-x
ai−x的出现次数即可。
有一个特殊情况:当 x = 0 x=0 x=0 时 i = j i = j i=j 也可以。但是题目貌似没规定是否可以相等。如果可以相等就不用改,如果不能相等就得特判 - 1.
此时复杂度显然就是 O ( n ) O(n) O(n)的了
实现
注意,在实现的过程中还是需要注意很多细节问题。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e6 + 5;
int a[maxn];
int b[maxn * 2]; // 注意:b的下标填的是值域范围,不是数组长度,所以需要开两倍
// 下标变换
int idTrans (int x){
// 这时下标从[-2e6,2e6]映射到[0,4e6]
return x + 2e6;
}
int main() {
int n , x;
cin >> n >> x;
for (int i = 1 ; i <= n ; i++)
cin >> a[i];
// 第一步:将数装进桶中
// 可以用STL中的unordered_map,但是我们发现下标其实没那么大
// 所以为了更快的运行速度我们可以开一个桶数组b。(题目只给了0.5s)
// 但是值域涉及到负数,所以需要做一个下标的映射.
for (int i = 1 ; i <= n ; i++){
b[idTrans(a[i])]++;
}
// 第二步:枚举每个数,统计答案
// 答案可能很大:考虑ai全等且x=0,那么任意两个i,j都是一个答案。那么答案会是n^2阶的。
// 尝试将其带进去会发现它爆int了,所以只能用long long 存储
long long ans = 0;
for (int i = 1 ; i <= n ; i++)
ans += b[idTrans(a[i] - x)];
cout << ans << endl;
return 0;
}
总结
评价
本题正式涉及到算法思想,鉴定为竞赛入门难度。
关键
这道题的优化关键在于桶预处理
拓展
1.将题目中的 a i − a j = x a_i-a_j= x ai−aj=x 改成 a i − a j = i − j a_i-a_j= i-j ai−aj=i−j
2.序列中有多少个子段的和恰好为
x
x
x,即求有多少有序对(
i
,
j
i,j
i,j)
i
<
j
i < j
i<j满足
∑
k
=
i
j
a
k
=
x
\sum_{k=i}^{j}a_k=x
k=i∑jak=x
3.序列中有多少个子段的和为
x
x
x的倍数,即求有多少有序对
(
i
,
j
(i,j
(i,j)满足
∑
k
=
i
j
a
k
≡
0
(
m
o
d
x
)
\sum_{k=i}^{j} a_k \equiv 0 \ (mod\ x)
k=i∑jak≡0 (mod x)
4.序列中有多少个子段的平均值恰好为
x
x
x,即求有多少有序对
(
i
,
j
(i,j
(i,j)满足
∑
k
=
i
j
a
k
j
−
i
+
1
=
x
\frac{\sum_{k=i}^{j} a_k}{j-i+1}=x
j−i+1∑k=ijak=x
题目内容均收集自互联网,如如若此项内容侵犯了原著者的合法权益,可联系我: (CSDN网站注册用户名: 塔子哥学算法) 进行删除。