容斥原理
不看每项的系数,容斥原理公式的每一项合起来,其实是把所有情况都选择了一遍(只选一个,只选两个,只选三个,只选四个…),除了一个也不选的情况。然后每项的系数,随着选中数目的增加,在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;
}