容斥原理
#
可能容斥原理的公式等还是 的巨佬讲得详细,大家可以看看这篇博客。
这篇博客把我写得手残疾了。
我这里直接上公式:
【例题 】#
不要看这个难度就吓跑了,这题综合性比较强,但每个知识点我相信大家都会的,只不过综合起来就变成这个难度了。
先在这里明确一下(与题目翻译有所不同): 表示花瓶的个数, 表示需要的花的数量, 表示第 个花瓶花的朵数。
【题目大意】#
让我们求出序列 的个数。
其中序列 要满足以下条件:
- 。
- 。
【分析】#
如果我们去除条件 ,则让我们求 的非负整数解的个数。
首先我们把 ,则我们要求出 的正整数解的个数。
即求不定方程正整数解的个数,这个可以用隔板法做。
形象化的说:
中放入 个隔板(至于为什么是 个隔板,我都不知道该怎么讲,可以滚回小学去了)。
那么我们要从 个空隙中(至于为什么是 个空隙,我依旧不知道怎么讲)选出 个空隙放隔板,那么这个就是小学奥数题,即为 (这玩意就等于 )。
然后加上条件 ,对于一个不合法的解至少有一个 满足 。
我们把 的 表示成一个集合 。
则不合法的解为一开始的公式(我只不过复制了一下):
则答案就为:
然后问题又来了,如果求上面的答案呢。
我们发现如果 不合法必定是 这个就为 ,那么我们把 减去 。
那么不管你剩余的 个花怎么选都是 的。
这里是没有 进行限制,那么这用回到了之前求不定方程解的个数。
答案就为 (这玩意就为 ),如果看到这里模糊的话请仔细阅读起那面求不定方程的过程。
那么如果是 也是同理。
要同时满足 和 ,那么和上面一样减去 和 。
然后再从剩余的 朵花中去选。
答案为 (这玩意就为 )。
然后我们把答案再写一下:
这个组合数的求法就按照 ,因为 ,与 同级,所以时间复杂度为 。
当然这个要用逆元即 , 为 的逆元即为 , 为模数(证明可以去百度搜费马小定理)。
然后那个柿子可以用二进制枚举来求,如果二进制中第 位是 表示要属于集合 。
时间复杂度:,空间复杂度:。
#
#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;
}
【例题 】#
这题我就不写得像前一题那么详细。
先来考虑如果没有 限制, 的含义见题意。
那么就是一道完全背包的模板题。
如果加上 限制,我们将 的 加入集合 ( 为第 张纸牌拿的张数)。
表示用这些无穷无尽钱凑出价值为 的方案数。
那么答案为:
我们也用上题的思路去求,将 张钱拿去,那么 应该减去 , 为价值, 为钱的面值。
然后问题转换成了求没有 限制,且价值为 的方案数,这也是完全背包的裸题。
等不展开了,具体见柿子。
我们再写一遍答案:
#
#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;
}
}
【例题 】#
#
先进行转化,
也就是让 。
再进行转化,就是让 并且 。
我们先不考虑条件 ,那么答案为 。
然后我们再加上条件 ,那么我们将 ,则我们将 分解质因式后为 。
然后根据容斥原理,即减去所有的 。
然后我们再书写一遍答案:
其中 表示 ,即 中所有 的倍数的个数。
因为 ,所以 。
当然这里要加点小优化,即如果 时,那么直接输出 ,不用做下面计算,否则你会喜提 TLE
。
因为满足 的序列,一定满足 是单调递增的,那么 不会被卡满,大约是 。
#
#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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具