题目链接
题目描述
牛客推出了三个题单:新手入门、算法入门、算法进阶。 给定以下五个整数:
: 至少刷过任意一个题单的总人数
: 刷过「新手入门」的人数
: 刷过「算法入门」的人数
: 刷过「算法进阶」的人数
: 恰好刷过其中任意两个题单的总人数
数据保证存在唯一非负整数解。请计算同时刷过全部三部曲的人数。
输入:
- 第一行一个整数
,表示测试用例数量。
- 接下来
行,每行五个整数
。
输出:
- 对每个测试用例,输出一行,表示同时刷过全部三个题单的人数。
解题思路
这是一个典型的集合问题,可以使用容斥原理(Principle of Inclusion-Exclusion)来解决。
我们设三个集合 分别代表刷过「新手入门」、「算法入门」、「算法进阶」的人群。
根据容斥原理,三个集合的并集大小为:
我们将题目中给出的变量代入这个公式:
我们要求的是同时刷过三个题单的人数,即 。我们将其设为
。
公式变为:
接下来处理 ,即恰好刷过两个题单的人数。这个值等于“刷过A和B但没刷过C的人”+“刷过A和C但没刷过B的人”+“刷过B和C但没刷过A的人”。
用集合运算表示:
整理后得到:
这样我们就可以用 和
表示两两相交的集合之和:
现在,将这个结果代入最初的容斥原理公式中:
最后,我们整理这个方程,解出我们想要的 :
由于题目保证存在唯一非负整数解,因此我们直接使用这个公式计算即可。使用 long long
或 long
可以避免中间和过大导致溢出。
代码
#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)
算法及复杂度
- 算法:数学、容斥原理
- 时间复杂度:
,每个测试用例是
的计算。
- 空间复杂度:
。