容斥原理及证明
定理
设共有个集合,表示第个集合,则所有集合的并集可表示成以下形式:
证明
设某个元素被个集合包含,显然地,其对左式的贡献为1,因为在并集中只计算一次。
考虑其对于右式的贡献,它会在这个集合的所有子集中被计算到。其贡献为:
由于所有元素对于左右两式的贡献均为1,综上即可证得等式成立。
推论
设表示的补集,表示全集,则:
常用于解决限制条件较繁复的问题。
相关练习
[JSOI2015]染色问题
题解
[click]
这三个限制可以逐个来解决,然后逐层相乘。
当至少有行列不染色的且规定为某行列不染色的时候,设表示不用种颜色的方案。
则:
左式的意义为,选个颜色不用的方案数。由于还有不涂色的选择,所以并不能保证只有那几个没有用到。假设有一种方案恰好有种颜色没用到,根据这样的选择方式,它就被计算了次。由此得出上式。
二项式反演一下,或者从单纯的容斥角度而言,即可得到:
合法的方案数即为:
这样的方案数,还是会算重,同样因为有不涂色的选择会导致超过行或列是完全空白的。再进行容斥,即可得到:
如果不将后面的求和转化掉的话,复杂度是。转化后对每一个的幂进行预处理,即可达到的复杂度。
代码
[click]
#include <cstdio>
#include <cctype>
typedef long long ll;
const int p=1e9+7;
const int maxn=400+10;
int fac[maxn],inv[maxn],pow[maxn*maxn];
int max(int x,int y) {return x>y?x:y;}
int read()
{
int res=0;
char ch=getchar();
while(!isdigit(ch))
ch=getchar();
while(isdigit(ch))
res=res*10+ch-'0',ch=getchar();
return res;
}
int power(int a,int n)
{
int res=1;
while(n)
{
if (n&1)
res=(ll)res*a%p;
a=(ll)a*a%p;
n>>=1;
}
return res;
}
void prework(int n)
{
fac[0]=1;
for (int i=1;i<=n;i++)
fac[i]=(ll)fac[i-1]*i%p;
inv[n]=power(fac[n], p-2);
for (int i=n-1;i>=0;i--)
inv[i]=(ll)inv[i+1]*(i+1)%p;
}
int C(int n,int m)
{
if (m>n)
return 0;
return (ll)fac[n]*inv[m]%p*inv[n-m]%p;
}
int main()
{
int n=read(),m=read(),c=read();
prework(max(max(n, m), c));
int ans=0;
pow[0]=1;
for (int k=0;k<=c;k++)
{
for (int i=1;i<=n;i++)
pow[i]=(ll)pow[i-1]*(c-k+1)%p;
for (int i=0;i<=n;i++)
{
int mul=(ll)power(pow[n-i]-1, m)*C(n, i)%p*C(c, k)%p;
if ((i^k)&1)
ans-=mul;
else
ans+=mul;
if (ans<0)
ans+=p;
else if (ans>=p)
ans-=p;
}
}
printf("%d\n",ans);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】