容斥原理

不看每项的系数,容斥原理公式的每一项合起来,其实是把所有情况都选择了一遍(只选一个,只选两个,只选三个,只选四个…),除了一个也不选的情况。然后每项的系数,随着选中数目的增加,在1和-1之间交替。

练习:

Devu 有 N 个盒子,第 i 个盒子中有 Ai 枝花。

同一个盒子内的花颜色相同,不同盒子内的花颜色不同。

Devu 要从这些盒子中选出 M 枝花组成一束,求共有多少种方案。

若两束花每种颜色的花的数量都相同,则认为这两束花是相同的方案。

结果需对 109+7 取模之后方可输出。

输入格式
第一行包含两个整数 N 和 M。

第二行包含 N 个空格隔开的整数,表示 A1,A2,…,AN。

输出格式
输出一个整数,表示方案数量对 109+7 取模后的结果。

数据范围
1≤N≤20,
0≤M≤1014,
0≤Ai≤1012
输入样例:
3 5
1 3 2
输出样例:
3

#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

typedef long long LL;

const int N = 20, mod = 1e9 + 7;

LL A[N];
int down = 1;

int qmi(int a, int k, int p)
{
    int res = 1;
    while (k)
    {
        if (k & 1) res = (LL)res * a % p;
        a = (LL)a * a % p;
        k >>= 1;
    }
    return res;
}

int C(LL a, LL b)
{
    if (a < b) return 0;
    int up = 1;
    for (LL i = a; i > a - b; i -- ) up = i % mod * up % mod;

    return (LL)up * down % mod; // 费马小定理
}

int main()
{
    LL n, m;
    cin >> n >> m;
    for (int i = 0; i < n; i ++ ) cin >> A[i];

    for (int j = 1; j <= n - 1; j ++ ) down = (LL)j * down % mod;
    down = qmi(down, mod - 2, mod);

    int res = 0;
    for (int i = 0; i < 1 << n; i ++ )
    {
        LL a = m + n - 1, b = n - 1;
        int sign = 1;
        for (int j = 0; j < n; j ++ )
            if (i >> j & 1)
            {
                sign *= -1;
                a -= A[j] + 1;
            }
        res = (res + C(a, b) * sign) % mod;
    }

    cout << (res + mod) % mod << endl;

    return 0;
}


达达正在破解一段密码,他需要回答很多类似的问题:

对于给定的整数 a,b 和 d,有多少正整数对 x,y,满足 x≤a,y≤b,并且 gcd(x,y)=d。

作为达达的同学,达达希望得到你的帮助。

输入格式
第一行包含一个正整数 n,表示一共有 n 组询问。

接下来 n 行,每行表示一个询问,每行三个正整数,分别为 a,b,d。

输出格式
对于每组询问,输出一个正整数,表示满足条件的整数对数。

数据范围
1≤n≤50000,
1≤d≤a,b≤50000
输入样例:
2
4 5 2
6 4 3
输出样例:
3
2
提示:gcd(x,y) 返回 x,y 的最大公约数。


#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

typedef long long LL;

const int N = 50010;

int primes[N], cnt;
bool st[N];
int mobius[N], sum[N];

// 线性筛法,求莫比乌斯函数
void init(int n)
{
    mobius[1] = 1;
    for (int i = 2; i <= n; i ++ )
    {
        if (!st[i])
        {
            primes[cnt ++ ] = i;
            mobius[i] = -1;
        }
        for (int j = 0; primes[j] * i <= n; j ++ )
        {
            int t = primes[j] * i;
            st[t] = true;
            if (i % primes[j] == 0)
            {
                mobius[t] = 0;
                break;
            }
            mobius[t] = mobius[i] * -1;
        }
    }

    for (int i = 1; i <= n; i ++ ) sum[i] = sum[i - 1] + mobius[i];
}

int main()
{
    init(N - 1);

    int T;
    scanf("%d", &T);
    while (T -- )
    {
        int a, b, d;
        scanf("%d%d%d", &a, &b, &d);
        a /= d, b /= d;
        int n = min(a, b);
        LL res = 0;
        for (int l = 1, r; l <= n; l = r + 1)
        {
            r = min(n, min(a / (a / l), b / (b / l)));
            res += (sum[r] - sum[l - 1]) * (LL)(a / l) * (b / l);
        }

        printf("%lld\n", res);
    }

    return 0;
}

posted @ 2022-03-12 09:22  PassName  阅读(278)  评论(0编辑  收藏  举报