容斥原理

157 篇文章 0 订阅

例:计算含有某几个质因子的数的个数

#include<cstdio>
#define maxn 20
using namespace std;

int n,k,a[maxn],ans=0;

void dfs(int now,int sum,int typ)
{
	if(now == k+1){ if(sum > 1)ans += (n / sum) * typ;return; }
	dfs(now+1,sum,typ);
	dfs(now+1,sum*a[now],-typ);
}

int main()
{
	scanf("%d%d",&n,&k);
	for(int i=1;i<=k;i++) scanf("%d",&a[i]);
	dfs(1,1,-1);
	printf("%d\n",ans);
}

上面那个东西当然是拿来搞笑的。
最近对容斥原理有了较深的理解,前来补博客。
容斥原理对于大多数人来说是一个原理,因为这个事实(对于普通人来说)太显然了,你想想,满足某一个条件的元素个数-满足某二个条件的元素个数+满足某三个条件的元素个数。。。。。显然是对的吧。
于是大多数人就这么去把它当做一个()原理去套了,然后被毒瘤出题人折磨致死。

所以这个东西是需要证明的。
其实容斥原理就是一种反演。
因为你是用至少(多)满足某几个条件的方案数来推出刚好满足某几个条件的方案数。
令f(sta) = 至少(多)满足sta条件的方案数 (sta是一个集合)
令g(sta) = 刚好满足sta条件的方案数 (sta是一个集合)
如果是至少,那么f(sta) = sigma g(ista) (ista & sta = sta)
则 g(空) = f(空) - f(1) - f(2) - f(3) … + f(1,2) + f(2,3)…
这个就显然多了对吧。。。。。。
相应的g(sta) = sigma( (-1)|tmp| * f(sta | tmp) , tmp & sta = 0)

这就是容斥原理的基本形式。
剩余其他的奇怪题目都是它的变形。
可以发现上面的形式是2^n的,效率很低。。。。。。
(其实,用的更加广泛的是减去补集。。。。)

但是容斥原理的原理并不在这,原理在于每一个元素的总(正负加起来)贡献是1。。。。。。

类型1:
如果f(sta) = H(|sta|) (这句话的意思是f函数(至少满足sta的方案数)的值只和状态的元素个数有关,所有元素互相等价的情况下)可以令alpha(k) = H(k) * C(n,k) (共有n个条件,选k个条件,再求至少满足这k个条件的方案数)可以发现对于某一个sta,f(sta) 会被 alpha(k) 计算C(|sta|,k)次。(注意alpha没有任何其他意义,因为会算重,它只是我们推公式的一个跳板而已)那么设只满足k个条件的方案为g(k)g(k) = alpha(k) - C(k+1,k) * alpha(k+1) - (-C(k+2,k+1) * C(k+1,k)) * alpha(k+2) ...别看这公式华美,这个就是一个去重 alpha(k+1) 被 alpha(k)计算C(k+1,k)次,所以去掉。alpha(k+2)被C(k+1,k)个alpha(k+1)计算C(k+2,k+1)次....然后你会发现C(k+2,k+1) * C(k+1,k) = C(k+2 , k).然后推一些式子就可以发现.g(k) = sigma( (-1)p-k * alpha ( p ) * C(p,k) , k<=p<=n)这个(居然)叫广义容斥原理。特殊的,g(0) = alpha(0) - alpha(1) + alpha(2) - alpha(3) ...

这个就是一般人用常识推出来的容斥原理了。
一般中规中矩的容斥原理题都是这个类型。
其实上面的公式还有另外一种理解方法:
alpha(k) = sigma( C(q,k) * g(q) )
然后通过二项式反演可得:
g(k) = sigma( (-1)q+k * C(q,k) * alpha(k) )
这是一样的。

例:
[bzoj4487][JSOI2015]染色问题
题目大意
有一个n*m的网格,你有p种颜色,每个格子可以不涂色或涂p种颜色中的一种。满足以下条件
1、每行都要有格子被涂
2、每列都要有格子被涂
3、每种颜色都得用

这个题就是非常典型的容斥原理题。
3大种不同的条件,但是同一大种条件中每一小种都互相等价。
1.设某一行没有格子被涂为一个a种条件
2.设某一列没有格子被涂为一个b种条件
3.设某一种颜色没有被涂为一个c种条件
g(a,b,c) = (-1)a * (-1)b * (-1)c * alpha(a,b,c)
alpha(a,b,c) = C(n,a) * C(m,b) * C(p,c) * (n*m)c+1
答案就是求g(0,0,0)

例2:
[bzoj4487][JSOI2015]染色问题
题目大意
有2^n个集合,每个集合只包含[1,n],且这些集合两两不同。问有多少种选择方法(至少选一个),使得这些集合交集大小为k。

上一个题用一般人的办法好像也可以理解,但是求满足恰好k个条件,很多人就不会做了,其实就是求g(k)
g(k) = alpha(k) - C(k+1,k) * alpha(k+1)…
alpha(k) = C(n,k) * (22^(n-k)-1)(不会用csdn打幂套幂啊。。。)

其实容斥原理更深的本质就是去重,套一个容斥系数算简单的函数的答案组合起来。
所以上面的情形这是一种常用的容斥原理模型,更加广泛的运用就得把容斥系数活用起来,
研究不同的容斥系数模型是必要的,但是这些模型都是在集合容斥原理的基础上建立的,无论是用条件个数容斥,还是用集合容斥,答案对才是硬道理。。。。。。。

到这应该就能理解了,容斥系数就是通过构造,让每一个元素都只贡献它应该贡献的值。
在上面的广义容斥原理中,每一个满足恰好p个条件的元素都对g(0)贡献了sigma( (-1)k * C(p,k) )
这个式子在p=0时值为1,其他时候为0。
那么同理对g(q)的贡献: sigma((-1)k-q * C(p,k) * C(k,q) ) = sigma((-1)k-q * C(p-q,k-q)) * C(p,q)
这个也是在p=q时值为1,其他时候为0。

这又是容斥原理的另一种理解方式。
现在我们有两种计算容斥系数的方法,第一种是直接去重,第二种是构造容斥系数。

下面就可以开始了:
满足恰好k个条件的一种方案的贡献为a[k],求总贡献。。。。
还是alpha(k)会将sta 算 C(|sta|,k)次。
然后我们来算alpha(k)在答案中的系数seq(k),
sigma( seq(k) * C(|sta|,k) , k<=sta ) = a[|sta|] 对所有sta都满足。
显然只和|sta|有关,n^2爆算seq(k)。
或者 sigma( seq(k) * C(|sta|,|sta| - k) , k<=sta ) = a[|sta|]
这个都很明白要干什么事情吧,多项式除法(求逆) O(nlogn)
比如说?
错排问题,1~n,如果有k个位置满足错排,则价值定义为a[k],求所有排列的价值和。(n<= 100000)
alpha(k) = C(n,k) * (n-k)! = n! / k!
好像有点毒瘤,但是有点板,有点套路。

下面就是一些常见容斥系数模型(学习自WerKeyTom_FTD):
1: 组合数形式 枚举约束的子集
2 : 斯特林数形式 枚举划分的子集

还有一种奇怪的容斥

参考博客:
https://blog.csdn.net/kele52he/article/details/77372902
https://blog.csdn.net/werkeytom_ftd/article/details/74701513

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
容斥原理是一种计数方法,用于解决集合中某些对象的数目的问题。它的基本思想是先计算包含于某个内容中的所有对象的数目,然后排除重复计算的对象,以确保计数结果既不遗漏又没有重复。在容斥原理的应用中,通常需要先求出所有包含的区间,然后使用欧拉函数求和,并进行一些补充操作,例如乘以某个系数或减去一个常数。在使用容斥原理时,需要注意一些细节,例如在枚举因子时要注意使用最小公倍数(LCM),而不是直接相乘。如果容斥问题中的集合可能包含0,还需要特殊处理。至于如何使用容斥原理在MATLAB中求解具体的问题,我需要更多的上下文信息才能给出具体的指导。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [容斥原理](https://blog.csdn.net/ling_wang/article/details/80488797)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [容斥原理练习记录](https://blog.csdn.net/z631681297/article/details/81318279)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值