「学习笔记」容斥原理

引入

A1:学语文的人, A2:学数学的人,A3:学英语的人,A4:学 OI 的人

A1A2:同时学语数的人

A1A2:学语文或数学的人

|A1A2|=|A1|+|A2||A1A2|

|A1A2A3|=|A1|+|A2|+|A3||A1A2||A1A3||A2A3|+|A1A2A3|

|A1A2A3A4|=|A1|+|A2|+|A3|+|A4||A1A2||A1A3||A2A3||A1A4||A2A4||A3A4|+|A1A2A3|+|A1A2A4|+|A2A3A4||A1A2A3A4|


我总结了一句话

容斥原理,就是总共的减去重复的加上缺失的。

容斥原理的一般式

b{A1,A2,,An}(1)|B|+1|aiBAi|

问题

n 对夫妻坐成一圈,每对夫妻不能坐在一起,问方案数。

总的方案数为 2n!2n=(2n1)!
我们将夫妻绑定在一起,这样,这对夫妻可以看作是一个人。
来计算不合法方案数:
一对夫妻坐在一起时,方案数为 (2n2)!,在 n 对夫妻中选一堆绑定,并且夫妻之间也有坐的顺序,因此一对夫妻坐在一起的非法方案数为 2C(n,1)(2n2)!
但是,在这一对夫妻坐在一起的方案数中,包含了两对夫妻坐在一起、三对夫妻坐在一起的情况,因此,有重复计算的,两对夫妻坐在一起被计算了两次,三对夫妻坐在一起被计算了三次。
我们要减去两对夫妻坐在一起的情况的方案数,即 22C(n,2)(2n3)!,在这两对夫妻坐在一起的情况里,同样,也包含了三对夫妻坐在一起的情况,而这一减,三对夫妻坐在一起的方案数直接变为 0 了,因此我们需要再把他们加上。
由此,就能够看出有容斥的规律了,我们往后推,减去四对夫妻坐在一起的方案数,加上 5 对夫妻坐在一起的方案数。。。
因此,总的非法方案数就是 2C(n,1)(2n2)!C(n,2)(2n3)!22+C(n,3)(2n4)!23
总的方案数减去非法方案数就是 (2n1)!2C(n,1)(2n2)!+C(n,2)(2n3)!22C(n,3)(2n4)!23

i=0nC(n,i)(2ni1)!2i(1)i


询问 1n 中有多少个数可以表示为 xyy>1 的形式。(n1018)

由于 long long 的范围可知,可行的 y 小于等于 64
在这 n 个数中,能表示成 x2 的数有 n 个,能表示成 x3 的数有 n3 个,能表示成 xy 的数有 ny 个。
但是,我们的答案就是 i=2yni 吗?
很明显,不是。
看一看这种情况,y6=(y3)2=(y2)3,很明显,直接求和会有重复,因此,我们要减去重复的部分

cin >> n;
for (int a = 2; a <= 64; ++ a) {
num[a] = 0;
// num[a] 代表 x^a 这种形式的数被算了几次
}
for (int a = 2; a <= 64; ++ a) { // 算 x^a 这种形式的数有多少个
ll v = floor(pow(n, (long double)1.0 / a)) - 1;
// ll v = (ll)floor(exp(log(n) / a)) - 1;
int d = 1 - num[a];
ans += v * d;
for (int b = a; b <= 64; b += a) {
num[b] += d;
}
}

代码解释

num[a] 代表 xa 这种形式的数被算了几次,很明显,我们希望最后每个 num 都为 1,因此,我们需要加上少的或者减去多的。

int d = 1 - num[a];
ans += v * d;`

最后,我们再更新 num

for (int b = a; b <= 64; b += a) {
num[b] += d;
}

这段代码可以这么理解
假设我们计算 x2,我们会算到 224282,我们可以将它们转化一下,即 222426,因此,只要是指数是二的倍数的形式的数,我们都能算到。

真题

[春季测试 2023] 幂次
这道题对于 pow 有精度要求,要用 long double,或者用 exp,否则最后一个点过不去。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

ll n, k, ans;
ll num[70];

int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> n >> k;
for (int a = k; a <= 64; ++ a) {
num[a] = 0;
}
for (int a = k; a <= 64; ++ a) {
ll v = floor(pow(n, (long double)1.0 / a)) - 1;
// ll v = (ll)floor(exp(log(n) / a)) - 1;
ll d = 1 - num[a];
ans += v * d;
for (int b = a; b <= 64; b += a) {
num[b] += d;
}
}
cout << ans + 1 << '\n';
return 0;
}

__EOF__

  • 本文作者: yi_fan0305
  • 本文链接: https://www.cnblogs.com/yifan0305/p/17454094.html
  • 关于博主: 四叶草少年
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
  • 声援博主: 如果您觉得文章对您有帮助,可以点击文章下方的【推荐】一下。
  • © 版权声明
    THE END
    喜欢就支持一下吧
    点赞0

    Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MY1ZEW88' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
    admin的头像-五八三
    评论 抢沙发
    头像
    欢迎您留下宝贵的见解!
    提交
    头像

    昵称

    图形验证码
    取消
    昵称代码图片