首页 > 其他分享 >容斥原理

容斥原理

时间:2024-04-25 13:00:31浏览次数:34  
标签:元素 int 补集 容斥 排列 集合 原理

容斥原理:

容斥原理是一种在知道所有集合之间的交,求集合之间的并的数学方法。(注:交即为两个集合之间相同的部分,记作 \(|A| \cap |B|\) )

problem:

设 \(U\) 中元素有 \(n\) 种不同的属性,而第 \(i\) 种属性称为 \(P_i\),拥有属性 \(P_i\) 的元素构成集合 \(S_i\),现在请求出 \(U\) 中有哪些元素。

易知:

也就是:

(摘自 OI-Wiki)

不定方程非负整数解计数:

problem:

给定 \(n\) 个变量,第 \(i\) 个记作 \(x_i\)。对于 \(x_i\) 有限制 \(x_i \le a_i\) 。且对于所有 \(x\) ,有 \(x_1+x_2+...+x_n=m\) ,问有多少组解。

solution:(by OI-Wiki)

对于这个问题,我们可以抽象出一个容斥模型:

1.全集:$\sum ^n_{i=1}x_i=m $ 解的个数。

2.集合:\(S_i=x_i\)

3.属性:即为限制条件。

解即为 $|\bigcap ^n_{i=1} S_i| $

我们可以通过求这个解集的补集,并用全集减去补集即可。

易知解集的补集为:\(|\bigcup^n_{i=1} !S_i|\)(不会补集怎么打) 使用插板法+容斥原理即可。

例题

贴个代码:

#include<bits/stdc++.h>
#define int long long
using namespace std;

const int mod=1e9+7;

int n,m,a[50],ans;
int inv(int x);
int c_zh(int x,int y);

signed main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++)cin>>a[i];
	for(int s=1;s<=(1<<n)-1;s++){
		int cnt=0,sum=0;
		for(int i=0;i<n;i++){
			if(s&(1<<i)){
				sum+=a[n-i]+1;
				cnt++;
			}
		}
		int f=-1;
		if(cnt&1)f=1;
		ans=(ans+f*c_zh(n-1,m-sum-1+n)%mod+mod)%mod;//这里是因为将限制减掉后变成了 0 ,所以还要加上n。
	}
	cout<<(c_zh(n-1,m-1+n)%mod-ans%mod+mod)%mod<<endl;
	return 0;
}
int fast_pow(int x,int y){
	int ret=x%mod,res=1;
	while(y){
		if(y&1)res*=ret;
		ret*=ret;
		res%=mod;ret%=mod;
		y>>=1;
	}
	return res;
}
int inv(int x){
	return fast_pow(x,mod-2);
}
int c_zh(int x,int y){
    if(y<0)return 0;
	int res=1;
	for(int i=1;i<=x;i++){
		res=(res*((y-i+1)%mod))%mod;// 注意这个可能溢出
	}
	for(int i=1;i<=x;i++){
		res=(res*inv(i))%mod;
	}
	//cout<<"x: "<<x<<" y: "<<y<<" ans: "<<res<<endl;
	return res;
}

错位排列:

problem:对于一个有 \(n\) 个元素的排列 \(P\) ,如果对于所有 \(i \le n\) , 都有 \(p_i \ne i\),则说排列 \(P\) 是一个错位排列。问对于有 \(n\) 个元素的序列有多少个错排列。

solution:

还是考虑使用全集 - 补集的方法去求解。

模型:

1.全集:\(n\) 个元素的排列:\(n!\)

2.集合:\(P_i\)

3.属性:\(P_i \ne i\)

那么很容易发现 \(P_i\) 的补集 \(U_i\) 即为满足 \(P_i=i\) 的一种解集。

所以答案即为:

\(n!-|\bigcup ^n_{i=1}U_i|\)

通过容斥原理以及组合数的定义,我们可以知道答案为(这里不再推式子):

\(n!\sum^n_{i=0}\frac{(-1)^i}{i!}\)

标签:元素,int,补集,容斥,排列,集合,原理
From: https://www.cnblogs.com/little-corn/p/18157425

相关文章

  • mock的原理是什么?
    Mocking是软件测试中常用的一种技术,它的原理是通过模拟(模仿)外部依赖或对象的行为,以便在测试中隔离被测系统的部分,使得测试更加简单、可控、可重复。Mocking的原理可以简单描述如下:替换外部依赖:在测试过程中,被测系统通常会依赖于外部组件、服务或对象,例如数据......
  • ELF文件格式解析器 原理 + 代码
    参考:https://bbs.kanxue.com/thread-259901.htm写在前面:   读《Linux二进制》,发现作者对ELF文件格式部分并没有做详细介绍,为了加深对elf文件格式理解,我自己着手写了个解析器,会和readelf工具协同对比。 原理:  ELF文件(目标文件)格式主要三种:1.可重定向文件(Re......
  • BOSHIDA DC电源模块的原理及工作方式
    BOSHIDADC电源模块的原理及工作方式DC电源模块是一种将交流电转换为直流电的设备,它将交流电输入端转换为稳定的直流电输出,以供电子设备使用。DC电源模块的工作原理及工作方式如下。 DC电源模块主要由以下几个主要组成部分构成:1.变压器:DC电源模块的输入端通常接收交流电,而......
  • springboot源码:容器启动过程(扩展业务对象、bean 生命周期)&动态注册自己的业务对象&
    0.SpringbootRun方法启动org.springframework.boot.SpringApplication#run(java.lang.String...)启动 publicConfigurableApplicationContextrun(String...args){ longstartTime=System.nanoTime(); DefaultBootstrapContextbootstrapContext=createBootstrap......
  • Java并发工具类之LongAdder原理总结
    出处: Java并发工具类之LongAdder原理总结LongAdder实现原理图                                高并发下N多线程同时去操作一个变量会造成大量线程CAS失败,然后处于自旋状态,导致严重浪费CPU资源,降低了并发......
  • 浅谈端口扫描原理
    一、端口扫描简介端口扫描,顾名思义,就是逐个对一段端口或指定的端口进行扫描。通过扫描结果可以知道一台计算机上都提供了哪些服务,然后就可以通过所提供的这些服务的己知漏洞就可进行攻击。其原理是当一个主机向远端一个服务器的某一个端口提出建立一个连接的请求,如果对方有此项服......
  • 【编译原理】原理笔记
    随便记点防止期末烂掉语法分析直接左递归的消除实际就是左递归转右递归法1:直接替换\[A\rightarrowA\alpha|\beta\Rightarrow\begin{cases}A\rightarrow\betaA',\\A'\rightarrow\alphaA'|\epsilon\end{cases}\]法2:矩阵法前置知识:\[I=\begin{pmatrix}\epsilo......
  • 将彩色图转化为灰度图及其原理介绍
    彩色图介绍彩色图像是一种包含颜色信息的图像,通常由红色、绿色和蓝色(RGB)三个颜色通道组成。这三种颜色通道可以叠加在一起来形成各种不同的颜色。彩色图像中的每个像素都有三个数值,分别表示红色、绿色和蓝色通道的强度或亮度。这三个数值通常在0到255之间,其中0代表没有该颜色通......
  • 加锁和释放锁的原理
    深入JVM看字节码,创建如下的代码:publicclassSynchronizedDemo2{Objectobject=newObject();publicvoidmethod1(){synchronized(object){}}} 使用javac命令进行编译生成.class文件>javacSynchronizedDemo2.java 使用j......
  • 混淆原理与实践指南
     引言......