P1102 A-B 数对

题目链接:

一开始的想法:排序后枚举,但这样显然是 \(O(n^2)\) 的复杂度,会超时

#include <cstdio>
#include <algorithm>

const int N = 2e5 + 5;

int a[N];

int main()
{
    int n, c, res = 0;
    scanf("%d%d", &n, &c);
    for (int i = 0; i < n; i++) scanf("%d", &a[i]);
    std::sort(a, a + n);
    for (int i = 0; i < n - 1; i++) {
        for (int j = i + 1; j < n; j++) {
            if (a[j] - a[i] == c) res++;
        }
    }
    printf("%d", res);
    return 0;
}

优化方式一: \(\rm map映射\) \(O(n)\)

\(A-B=C\) 变形为 \(A-C=B\)。用 \(\rm map\) 记录数组中每个元素的出现次数然后将每个元素都减去 \(c\),最后再遍历一遍数组求出当前所有元素分别在 \(\rm map\) 中的次数之和(即这些元素在原数组中的出现次数)。由于最坏情况可能达到 \(10^{10}\) 会爆 \(\rm int,\)因此要用 \(\rm long\) \(\rm long\)

#include <bits/stdc++.h>

using namespace std;
using LL = long long;

const int N = 2e5 + 5;

int a[N];
map<int, int> p;

int main()
{
    int n, c;
    LL res = 0;
    scanf("%d%d", &n, &c);
    for (int i = 0; i < n; i++) {
        scanf("%d", &a[i]);
        p[a[i]]++;
        a[i] -= c;
    }
    for (int i = 0; i < n; i++) 
        res += p[a[i]];
    printf("%lld", res);
    return 0;
}

优化方式二: \(二分\) \(O(nlog_n)\)

由于 \(A-B=C\) 可变形为 \(A-C=B\)\(A=B+C\),这样两边分别都含变量,我们每次枚举 \(A\),这样问题就转换为寻找数组中元素为 \(A-C\) 的个数或 \(B+C\) 的个数,可以用二分优化。
查询数组a中元素值为val的个数就是
upper_bound(a, a+n, val) - lower_bound(a, a+n, val);
大于这个元素的最小指针减去等于这个元素的最小指针就是这个元素的个数(一定要有序

以下为 \(AC\) 代码:

#include <bits/stdc++.h>

using namespace std;
using LL = long long;

const int N = 2e5 + 5;

int a[N];

int main()
{
    int n, c;
    LL res = 0;
    scanf("%d%d", &n, &c);
    for (int i = 0; i < n; i++) scanf("%d", &a[i]);

    sort(a, a + n);
    
    for (int i = 0; i < n; i++) {
        res += upper_bound(a, a + n, a[i] - c) - lower_bound(a, a + n, a[i] - c);//这里改成a[i]+c也一样可以AC
    }
    printf("%lld", res);
    return 0;
}

优化方式三、双指针(尺取法)

首先把数列升序排列,\(k\) 指针用于枚举每个数,\(i\) 指针指向 \(a[i]-a[k]=c\) 的一段数的左端,\(j\) 指向 \(a[j]-a[k]=c\) 的一段数的右端 + 1\(\rm ans\) 维护这段数的个数,ans += j - i.

由于 \(i\) 指针和 \(j\) 指针都是单调向右走的,最多走完整个序列长度,因此总的时间复杂度为 \(O(n)\)

#include <bits/stdc++.h>

using namespace std;
using i64 = long long;


int main()
{
	ios::sync_with_stdio(false), cin.tie(nullptr);
	int n, c;
	cin >> n >> c;
	vector<int> a(n);
	for (int i = 0; i < n; i++) cin >> a[i];
	sort(a.begin(), a.end());
	int i = 0, j = 0;
	i64 ans = 0;
	for (int k = 0; k < n; k++) {
		while (i < n && a[i] - a[k] < c) i++;
		while (j < n && a[j] - a[k] <= c) j++;
		ans += j - i;
	}
	cout << ans;
	return 0;
}
posted @ 2024-02-14 11:49  胖柚の工作室  阅读(25)  评论(0)    收藏  举报