【备战秋招】每日一题:2023.3.15-阿里OD机试(第二题)-极差三元组计数

文章描述了一个关于古董店主人与麻将的故事,其中涉及一个数学游戏。玩家需找到满足条件的三元组(i,j,k),使得最大值减去最小值等于1。解决方案是使用哈希表统计数字出现的次数,然后应用组合公式计算出合法的三元组数量。给定的代码示例展示了如何用不同编程语言实现这一算法。
摘要由CSDN通过智能技术生成

在线评测链接:P1083

题目内容

在一个小镇上,有一家古董店。这家古董店主人是一个极具收藏热情的老人,他一生都在收集和珍藏各种珍奇古董。他把他的珍藏分成了许多类别,其中包括了各种古代文物、稀有的艺术品和珍贵的文献资料等。

然而,这位老人在年轻时曾有一段失恋的经历,他的爱人因意外离世,让他深受打击。他渐渐地迷上了麻将这项运动,麻将成为了他减轻痛苦的一种方式。他甚至在自己的古董店里开了一间麻将室,邀请各位朋友前来打麻将。

一天,他在整理古董时发现了一组古董,这组古董是他年轻时收藏的。他把这些古董拿到麻将室,让朋友们来观赏和欣赏。这组古董是由 n n n 个不同的古董组成的,老人和他的朋友们都非常喜欢这些古董。

他在整理这些古董时,发现了一组数字序列 a 0 , a 1 , ⋯   , a n − 1 a_0,a_1,\cdots,a_{n-1} a0,a1,,an1 ,其中每个 a i a_i ai 表示古董的编号。

他想让朋友们玩一下游戏,要求朋友们找出有多少组三元组 < i , j , k > < i,j,k > <i,j,k> 满足 0 ≤ i < j < k < n 0\le i <j<k<n 0i<j<k<n m a x ( a i , a j , a k ) − m i n ( a i , a j , a k ) = 1 max(a_i,a_j,a_k) - min(a_i,a_j,a_k) = 1 max(ai,aj,ak)min(ai,aj,ak)=1

这时,老人问你是否能够帮助他计算出答案。

输入描述

第一行输入一个正整数 n n n

第二行输入 n n n个正整数 a i a_{i} ai

3 ≤ n ≤ 200000 3 \leq n \leq 200000 3n200000

1 ≤ a ≤ 1 0 9 1 \leq a \leq 10^9 1a109

输出描述

一个整数,代表合法的三元组数量。

样例

输入

5
3 2 1 2 3

输出

5
样例

输入

5
1 2 3 4 5

输出

0

思路

计数题

观察 m a x − m i n = 1 max - min = 1 maxmin=1,那么三元组中 必定有两个元素相等 ,结构如下两种

情况1 x − 1 , x , x x-1,x,x x1,x,x

情况2 x − 1 , x − 1 , x x-1,x-1,x x1,x1,x

那么我们可以使用一个桶(哈希表) d d d先统计每个数出现的次数。然后枚举每一个值 x x x.

第一种情况的贡献: C ( d [ x ] , 2 ) ∗ d [ x − 1 ] C(d[x] , 2) * d[x-1] C(d[x],2)d[x1]

第二种情况的贡献: C ( d [ x − 1 ] , 2 ) ∗ d [ x ] C(d[x-1] , 2) * d[x] C(d[x1],2)d[x]

累加起来即可.答案是 n 3 n^3 n3 级别的,记得开 l o n g   l o n g long\ long long long

类似题目推荐

塔子哥点评:今年春招考了很多这种计数题。80%出自牛客出题组之手(阿里,百度,蚂蚁,携程…).这类题在OI/ACM界一般也称作数数题

CodeFun2000

以下都是23春招考察到的数数题,难度依次递增

P1133 京东 2023.03.28-第二题-染色の数组

P1192 2023.04.13-腾讯音乐-暑期实习-第一题-平滑值

P1104 2023.03.21-阿里-第五题-任务分配

P1055 2023.2.25-京东-塔子哥的单词

P1099 2023.03.21-蚂蚁-第三题-塔子哥的排列权值之和

P1096 2023.03.19-米哈游-第三题-倍数集合

P1243 2023.04.20-蚂蚁暑期实习-第二题-RB字符串

P1126 2023.03.26-实习-腾讯-第五题-序列最大公约

代码

CPP

#include <bits/stdc++.h>
using namespace std;
#define ll long long
unordered_map<int , ll> d;
int main() {
    int n;
    cin >> n;
    ll ans = 0;
    // 读入 + 桶计数
    for (int i = 1 ; i <= n ; i++){
        int x;
        cin >> x;
        d[x]++;
    }
    // 公式参考上面
    for (auto g : d){
        int x = g.first;
        ans += (d[x] - 1) * d[x] / 2 * d[x - 1];
        ans += (d[x - 1] - 1) * d[x - 1] / 2 * d[x];
    }
    cout << ans << endl;
	return 0;
}

python

import collections

d = collections.defaultdict(int)
n = int(input())
ans = 0
# 读入 + 桶计数
for x in map(int,input().split()):
    d[x] += 1

# 公式参考上面
for x in sorted(d.keys()):
    ans += (d[x] - 1) * d[x] // 2 * d[x - 1]
    ans += (d[x - 1] - 1) * d[x - 1] // 2 * d[x]

print(ans)

Java

import java.util.*;
public class Main {
    public static void main(String[] args) {
        Scanner input=new Scanner(System.in);
        int n = input.nextInt();
        Map<Integer, Long> d = new HashMap<>();
        long ans = 0;

        // 读入 + 桶计数
        for (int i = 1 ; i <= n ; i++){
            int x = input.nextInt();
            d.put(x, d.getOrDefault(x, 0L)+1L);
        }

        // 公式参考上面
        for (Map.Entry<Integer, Long> entry : d.entrySet()) {
            int x = entry.getKey();
            long value = entry.getValue();
            if (d.containsKey(x-1)) {
                ans += (value - 1) * value / 2 * d.get(x-1);
            }
            if (d.containsKey(x+1)) {
                ans += (value - 1) * value / 2 * d.get(x+1);
            }
        }

        System.out.println(ans);
    }
}

Go

package main

import (
    "fmt"
    "sort"
)

func main() {
    var n int
    fmt.Scan(&n)

    d := make(map[int]int64)
    ans := int64(0)

    // 读入 + 桶计数
    for i := 1; i <= n; i++ {
        var x int
        fmt.Scan(&x)
        d[x] += 1
    }

    // 公式参考上面
    keys := make([]int, 0)
    for k := range d {
        keys = append(keys, k)
    }
    sort.Ints(keys)
    for _, x := range keys {
        if cnt, ok := d[x]; ok && cnt > 1 {
            ans += (cnt - 1) * cnt / 2 * d[x-1]
        }
        if cnt, ok := d[x-1]; ok && cnt > 1 {
            ans += (cnt - 1) * cnt / 2 * d[x]
        }
    }

    fmt.Println(ans)
}

Js

process.stdin.resume();
process.stdin.setEncoding('utf-8');
let input = '';
process.stdin.on('data', (data) => {
    input += data;
    return;
});
process.stdin.on('end', () => {
    const lines = input.trim().split('\n');
    let d = new Map();
    let ans = 0;
    let n = parseInt(lines[0]);
    // 读入 + 桶计数
    lines[1].trim().split(' ').forEach((x) => {
        x = parseInt(x)
        if (d.has(x)) {
            d.set(x, d.get(x) + 1);
        } else {
            d.set(x, 1);
        }
    });
    // 公式参考上面
    let keys = [...d.keys()].sort((a, b) => a - b);
    for (let i = 0; i < keys.length; i++) {
        let x = keys[i];
        // 因为x - 1不一定存在
        let tmp = d.get(x - 1) || 0;
        ans += (d.get(x) - 1) * d.get(x) / 2 * tmp;
        ans += (tmp - 1) * tmp / 2 * d.get(x);
    }
    console.log(ans);
});
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

塔子哥学算法

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值