容斥原理
\mathcal Preface
可能容斥原理的公式等还是
这篇博客把我写得手残疾了。
我这里直接上公式:
【例题 \mathit 1 】
Devu and Flowers。
先在这里明确一下(与题目翻译有所不同):
【题目大意】
让我们求出序列
其中序列
【分析】
如果我们去除条件
首先我们把
即求不定方程正整数解的个数,这个可以用隔板法做。
形象化的说:
#include <bits/stdc++.h>
#define x first
#define y second
#define IOS ios::sync_with_stdio(false)
#define cit cin.tie(0)
#define cot cout.tie(0)
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
const int N = 20, M = 100010, MOD = 1e9 + 7;
const int INF = 0x3f3f3f3f;
const LL LLINF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-8;
void solve();
int main()
{
IOS;
cit, cot;
int T = 1;
// cin >> T;
while (T -- ) solve();
return 0;
}
LL n, m;
LL q[N];
int qmi(int a, int k)
{
int res = 1;
while (k)
{
if (k & 1) res = (LL)res * a % MOD;
a = (LL)a * a % MOD;
k >>= 1;
}
return res;
}
int C(LL a, LL b)
{
if (a < b) return 0;
int x = 1, y = 1;
for (LL i = a - b + 1; i <= a; i ++ ) x = i % MOD * x % MOD;
for (int i = 1; i <= b; i ++ ) y = y * (LL)i % MOD;
return x * (LL)qmi(y, MOD - 2) % MOD;
}
void solve()
{
cin >> n >> m;
for (int i = 0; i < n; i ++ ) cin >> q[i];
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 -= q[j] + 1;
}
res = ((res + C(a, b) * sign) % MOD + MOD) % MOD;
}
cout << res << endl;
}
【例题 \mathit 2 】
硬币购物。
这题我就不写得像前一题那么详细。
先来考虑如果没有
那么就是一道完全背包的模板题。
如果加上
#include <bits/stdc++.h>
#define x first
#define y second
#define IOS ios::sync_with_stdio(false)
#define cit cin.tie(0)
#define cot cout.tie(0)
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
const int N = 100010, M = 100010, MOD = 1e9 + 7;
const int INF = 0x3f3f3f3f;
const LL LLINF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-8;
void solve();
int main()
{
IOS;
cit, cot;
int T = 1;
// cin >> T;
while (T -- ) solve();
return 0;
}
int c[4], n;
LL f[N];
void solve()
{
cin >> c[0] >> c[1] >> c[2] >> c[3] >> n;
f[0] = 1;
for (int i = 0; i < 4; i ++ )
for (int j = c[i]; j < N; j ++ )
f[j] += f[j - c[i]];
while (n -- )
{
int d[4], _s;
cin >> d[0] >> d[1] >> d[2] >> d[3] >> _s;
LL res = 0;
for (int i = 0; i < 1 << 4; i ++ )
{
int sign = 1;
LL s = _s;
for (int j = 0; j < 4; j ++ )
if (i >> j & 1)
{
sign *= -1;
s -= c[j] * (d[j] + 1ll);
}
if (s >= 0) res += sign * f[s];
}
cout << res << endl;
}
}
【例题 \mathit{3} 】
Count GCD。
\mathcal Solution
先进行转化,
也就是让
再进行转化,就是让
我们先不考虑条件
然后我们再加上条件
然后根据容斥原理,即减去所有的
然后我们再书写一遍答案:
其中
因为
当然这里要加点小优化,即如果 TLE。
因为满足
\mathcal Code
#include <bits/stdc++.h>
#define x first
#define y second
#define IOS ios::sync_with_stdio(false)
#define cit cin.tie(0)
#define cot cout.tie(0)
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
const int N = 200010, M = 100010, MOD = 998244353;
const int INF = 0x3f3f3f3f;
const LL LLINF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-8;
int n, m;
int a[N];
void solve();
int main()
{
IOS;
cit, cot;
int T = 1;
cin >> T;
while (T -- ) solve();
return 0;
}
void solve()
{
cin >> n >> m;
bool flag = true;
for (int i = 1; i <= n; i ++ )
{
cin >> a[i];
if (i > 1 && a[i - 1] % a[i])
{
flag = false;
}
}
if (!flag)
{
cout << 0 << endl;
return;
}
int res = 1;
for (int i = 2; i <= n; i ++ )
{
int k = m / a[i], t = a[i - 1] / a[i];
vector<int> p;
for (int j = 2; j <= t / j; j ++ )
if (t % j == 0)
{
p.push_back(j);
while (t % j == 0) t /= j;
}
if (t > 1) p.push_back(t);
int sum = 0, sz = p.size();
for (int i = 0; i < 1 << sz; i ++ )
{
int sign = 1, s = 1;
for (int j = 0; j < sz; j ++ )
if (i >> j & 1)
{
sign *= -1;
s *= p[j];
}
sum += sign * (k / s);
}
res = res * (LL)sum % MOD;
}
cout << res << endl;
}