题目链接

刷题统计

题目描述

牛客推出了三个题单:新手入门、算法入门、算法进阶。 给定以下五个整数:

  • : 至少刷过任意一个题单的总人数
  • : 刷过「新手入门」的人数
  • : 刷过「算法入门」的人数
  • : 刷过「算法进阶」的人数
  • : 恰好刷过其中任意两个题单的总人数

数据保证存在唯一非负整数解。请计算同时刷过全部三部曲的人数。

输入:

  • 第一行一个整数 ,表示测试用例数量。
  • 接下来 行,每行五个整数

输出:

  • 对每个测试用例,输出一行,表示同时刷过全部三个题单的人数。

解题思路

这是一个典型的集合问题,可以使用容斥原理(Principle of Inclusion-Exclusion)来解决。

我们设三个集合 分别代表刷过「新手入门」、「算法入门」、「算法进阶」的人群。 根据容斥原理,三个集合的并集大小为:

我们将题目中给出的变量代入这个公式:

我们要求的是同时刷过三个题单的人数,即 。我们将其设为 。 公式变为:

接下来处理 ,即恰好刷过两个题单的人数。这个值等于“刷过A和B但没刷过C的人”+“刷过A和C但没刷过B的人”+“刷过B和C但没刷过A的人”。 用集合运算表示:

整理后得到:

这样我们就可以用 表示两两相交的集合之和:

现在,将这个结果代入最初的容斥原理公式中:

最后,我们整理这个方程,解出我们想要的

由于题目保证存在唯一非负整数解,因此我们直接使用这个公式计算即可。使用 long longlong 可以避免中间和过大导致溢出。

代码

#include <iostream>

using namespace std;

void solve() {
    long long n, na, nb, nc, n2;
    cin >> n >> na >> nb >> nc >> n2;
    long long ans = (na + nb + nc - n2 - n) / 2;
    cout << ans << endl;
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    int t;
    cin >> t;
    while (t--) {
        solve();
    }
    return 0;
}
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int t = sc.nextInt();
        while (t-- > 0) {
            long n = sc.nextLong();
            long na = sc.nextLong();
            long nb = sc.nextLong();
            long nc = sc.nextLong();
            long n2 = sc.nextLong();
            long ans = (na + nb + nc - n2 - n) / 2;
            System.out.println(ans);
        }
    }
}
t = int(input())
for _ in range(t):
    n, na, nb, nc, n2 = map(int, input().split())
    ans = (na + nb + nc - n2 - n) // 2
    print(ans)

算法及复杂度

  • 算法:数学、容斥原理
  • 时间复杂度:,每个测试用例是 的计算。
  • 空间复杂度: