BGN27 谐距下标对
思路
先想一个问题:什么叫"谐距"?题目说的是下标 ,满足
,也就是两个元素的"值的差"恰好等于"下标的差"。你可以理解成:如果把数组画在坐标系上,这两个点刚好在同一条斜率为 1 的直线上。
那怎么找这样的下标对呢?暴力枚举所有 肯定能做,但
在数据量大的时候就扛不住了。
关键转化
把条件 移项一下:
$$
发现了吗?只要定义 ,那问题就变成了:数组
中有多少对相等的元素?
这就是经典的"计数相同元素对数"问题了。用哈希表统计每个值出现的次数 ,每个值贡献
对,加起来就是答案。
举个例子
输入 [1, 2, 3, 4, 5, 6],计算 (0 下标):
| 0 | 1 | 2 | 3 | 4 | 5 | |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | |
| 1 | 1 | 1 | 1 | 1 | 1 |
值全是 1,出现 6 次,所以答案是
。
代码
#include <bits/stdc++.h>
using namespace std;
int main(){
int n;
scanf("%d",&n);
unordered_map<int,long long> mp;
for(int i=0;i<n;i++){
int x;
scanf("%d",&x);
mp[x-i]++;
}
long long cnt=0;
for(auto&[k,v]:mp){
cnt+=v*(v-1)/2;
}
printf("%lld\n",cnt);
return 0;
}
import java.util.*;
import java.io.*;
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int n = Integer.parseInt(br.readLine().trim());
StringTokenizer st = new StringTokenizer(br.readLine().trim());
HashMap<Integer,Long> mp = new HashMap<>();
for (int i = 0; i < n; i++) {
int x = Integer.parseInt(st.nextToken());
int key = x - i;
mp.merge(key, 1L, Long::sum);
}
long cnt = 0;
for (long v : mp.values()) {
cnt += v * (v - 1) / 2;
}
System.out.println(cnt);
}
}
from collections import Counter
n = int(input())
a = list(map(int, input().split()))
c = Counter(x - i for i, x in enumerate(a))
print(sum(v * (v - 1) // 2 for v in c.values()))
const readline = require('readline');
const rl = readline.createInterface({ input: process.stdin });
const lines = [];
rl.on('line', line => lines.push(line));
rl.on('close', () => {
const n = parseInt(lines[0]);
const a = lines[1].split(' ').map(Number);
const mp = new Map();
for (let i = 0; i < n; i++) {
const key = a[i] - i;
mp.set(key, (mp.get(key) || 0) + 1);
}
let cnt = 0;
for (const v of mp.values()) {
cnt += v * (v - 1) / 2;
}
console.log(cnt);
});
复杂度
- 时间复杂度:
,遍历一次数组建哈希表,再遍历一次哈希表求和。
- 空间复杂度:
,哈希表最多存
个不同的键。
总结
这题的核心就一个等式变形: 变成
。一旦想到定义
,问题就退化成了"数组里有多少对相等元素",哈希表计数走起,
搞定。遇到"两个量的差等于另外两个量的差"这类条件,移项凑同侧是个很常用的套路,值得记住。



京公网安备 11010502036488号