#P2147. 2024.9.29-第3题-小红办事
          
                        
                                    
                      
        
              - 
          
          
                      1000ms
            
          
                      Tried: 112
            Accepted: 30
            Difficulty: 5
            
          
          
          
                       所属公司 : 
                              字节
                                
            
                        
              时间 :2024年9月29日
                              
                      
          
 
- 
                        算法标签>数学          
 
2024.9.29-第3题-小红办事
思路: 容斥原理
- 
计算每项活动单独发生的天数:
- 跑步的天数: ∣A∣=⌊an⌋
 - 打球的天数: ∣B∣=⌊bn⌋
 - 去图书馆的天数: ∣C∣=⌊cn⌋
 
 - 
计算两两活动同时发生的天数:
- 跑步和打球同时发生的天数: ∣A∩B∣=⌊lcm(a,b)n⌋
 - 跑步和去图书馆同时发生的天数: ∣A∩C∣=⌊lcm(a,c)n⌋
 - 打球和去图书馆同时发生的天数: ∣B∩C∣=⌊lcm(b,c)n⌋
 
 - 
计算三项活动同时发生的天数: ∣A∩B∩C∣=⌊n/lcm(a,b,c)⌋
 - 
应用容斥原理计算:
根据容斥原理,至少两项活动发生的天数为:
∣A∩B∣+∣A∩C∣+∣B∩C∣−2∣A∩B∩C∣这里减去两倍的三项交集,是因为三项交集的天数在两两交集中总共计算了三次,需要减去 2 倍。
 - 
另一种理解方法:
恰好两项活动发生的次数为:
∣A∩B∣+∣A∩C∣+∣B∩C∣−3∣A∩B∩C∣恰好三项活动发生的次数为:
∣A∩B∩C∣两项相加就是答案。
 
代码
java
import java.util.Scanner;
public class Main {
    // 计算最小公倍数
    public static long lcm(long x, long y) {
        return (x * y) / gcd(x, y);
    }
    // 计算最大公约数
    public static long gcd(long a, long b) {
        while (b != 0) {
            long temp = b;
            b = a % b;
            a = temp;
        }
        return a;
    }
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int T = scanner.nextInt();  // 读取测试用例的数量
        
        // 遍历每个测试用例
        for (int i = 0; i < T; i++) {
            long n = scanner.nextLong();  // 天数
            long a = scanner.nextLong();  // 跑步的间隔
            long b = scanner.nextLong();  // 打球的间隔
            long c = scanner.nextLong();  // 图书馆的间隔
            // 计算每对活动的最小公倍数
            long lcm_ab = lcm(a, b);
            long lcm_ac = lcm(a, c);
            long lcm_bc = lcm(b, c);
            long lcm_abc = lcm(lcm_ab, c); // 计算 a, b, c 的最小公倍数
            // 计算每对活动发生的次数
            long count_ab = n / lcm_ab;
            long count_ac = n / lcm_ac;
            long count_bc = n / lcm_bc;
            long count_abc = n / lcm_abc;
            // 根据容斥原理计算至少两项活动发生的天数
            long at_least_two = count_ab + count_ac + count_bc - 2 * count_abc;
            
            System.out.println(at_least_two);  // 输出结果
        }
        scanner.close();
    }
}
python
import math
def lcm(x, y):
    return (x * y) // math.gcd(x, y)
def lcm_three(a, b, c):
    return lcm(lcm(a, b), c)
def main():
    T = int(input())  # 读取测试用例的数量
    results = []
    
    for _ in range(T):
        n, a, b, c = map(int, input().split())  # 逐行读取 n, a, b, c
        
        # 计算每对活动的最小公倍数
        lcm_ab = lcm(a, b)
        lcm_ac = lcm(a, c)
        lcm_bc = lcm(b, c)
        lcm_abc = lcm_three(a, b, c)
        
        # 计算每对活动的天数
        count_ab = n // lcm_ab
        count_ac = n // lcm_ac
        count_bc = n // lcm_bc
        count_abc = n // lcm_abc
        
        # 根据容斥原理计算至少两项活动发生的天数
        at_least_two = count_ab + count_ac + count_bc - 2 * count_abc
        
        results.append(str(at_least_two))  # 收集结果
    
    # 输出所有结果
    print("\n".join(results))
if __name__ == "__main__":
    main()
cpp
#include <iostream>
#include <algorithm>
using namespace std;
// 计算最大公约数
long long gcd(long long a, long long b) {
    while (b != 0) {
        long long temp = b;
        b = a % b;
        a = temp;
    }
    return a;
}
// 计算最小公倍数
long long lcm(long long x, long long y) {
    return (x * y) / gcd(x, y);
}
int main() {
    int T;
    cin >> T;  // 读取测试用例的数量
    // 遍历每个测试用例
    for (int i = 0; i < T; i++) {
        long long n, a, b, c;
        cin >> n >> a >> b >> c;  // 逐行读取 n, a, b, c
        
        // 计算每对活动的最小公倍数
        long long lcm_ab = lcm(a, b);
        long long lcm_ac = lcm(a, c);
        long long lcm_bc = lcm(b, c);
        long long lcm_abc = lcm(lcm_ab, c);  // 计算 a, b, c 的最小公倍数
        // 计算每对活动发生的次数
        long long count_ab = n / lcm_ab;
        long long count_ac = n / lcm_ac;
        long long count_bc = n / lcm_bc;
        long long count_abc = n / lcm_abc;
        // 根据容斥原理计算至少两项活动发生的天数
        long long at_least_two = count_ab + count_ac + count_bc - 2 * count_abc;
        
        cout << at_least_two << endl;  // 输出结果
    }
    return 0;
}
OJ会员可以通过点击题目上方《已通过》查看其他通过代码来学习。
题目内容
小红从今天(第1天)开始每a天(第a天、第a+a天、……)会去跑步,每b天会去打球,每c天会去图书馆。
小红想知道从第1天到第n天中至少需要办两件事的天数。
输入描述
每个测试文件均包含多组测试数。第一行输入一个整数T(1≤T≤105),代表数据组数,每组 测试数据描述如下: 每行输入四个整数n,a,b,c(1≤n≤109;1≤a,b,c≤104),含义如题面所示。
输出描述
对于每一组测试数据,在一行上输出一个整数,代表n天中至少需要办两件事的天数。
样例1
输入
2
1 1 1 1
5 1 2 3
输出
1
3
说明
对于第一个样例,第1天至少需要办三件事。 对于第二个样例,其中一种解释是,小红会在第1,2,3,4,5天去跑步,在第2,4天去打球,第 3天去图书馆。所以第2,3,4天都至少需要办两件事以上。