<学习笔记> 二项式反演
二项式反演#
证明#
我们设
首先有 (组合数学6.2)
将补集看成原集,将原集看成补集,就有
可以知道
所以就有反演基本形式:
另
那么就可以得到最常用的形式
形式#
形式零
形式一(适用于设至多存在,
形式二(适用于设至少存在,
组合意义#
记
例题#
集合计数#
题目大意#
一个有
题解#
通过思考列出式子
接下来考虑上式与所求的关系:设
通过二项式反演求出
使用一些预处理手段,时间复杂度
这里对于
,则
Another Filling the Grid #
对于一行合法的话,答案就是
那么二项式反演有
code
// Another Filling the Grid
#include<bits/stdc++.h>
#define int long long
const int mod=1e9+7;
const int N=300;
int qpow(int x,int p){
int ans=1;
while(p){
if(p&1) ans=(ans*x)%mod;
x=(x*x)%mod;
p>>=1;
}
return ans;
}
int g[N+5];
int fac[N+5],inv[N+5];
void init(){
fac[0]=inv[0]=1;
for(int i=1;i<=N;i++) fac[i]=fac[i-1]*i%mod;
inv[N]=qpow(fac[N],mod-2);
for(int i=N-1;i>=1;i--) inv[i]=inv[i+1]*(i+1)%mod;
}
int C(int x,int y){
return fac[x]*inv[y]%mod*inv[x-y]%mod;
}
signed main(){
int n,k;
scanf("%lld%lld",&n,&k);
init();
for(int i=0;i<=n;i++){
g[i]=C(n,i)*qpow((qpow(k,n-i)*qpow(k-1,i)%mod-qpow(k-1,n)+mod)%mod,n)%mod;
}
int ans=0;
for(int i=0;i<=n;i++){
int tmp=((i)%2 ? -1: 1);
ans=(ans+tmp*C(i,0)*g[i]%mod+2*mod)%mod;
}
printf("%lld",ans);
}
[JLOI2016] 成绩比较#
考虑将这个分成两个部分考虑
1.计算在n-1个人中选出k个,被B神碾压的方案数并且对于剩下的n-1-k个人,计算有多少种方案来合法分配每一个人、每一门科目的得分状况。这里,得分状况定义为是比B神高,还是比B神低或相等。
2.已知每一门科目的得分状况,计算对于给定的满分,有多少种分配分数的方案。
对于第一部分
我们设
然后设
那么这一部分答案就是
对于第二部分
我们考虑
因为
code
// 成绩比较
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int mod=1e9+7;
const int N=1005;
int U[N+5],R[N+5];
int fac[N+5],inv[N+5];
int g[N+5],h[N+5];
int qpow(int x,int p){
int ans=1;
while(p){
if(p&1) ans=(ans*x)%mod;
x=(x*x)%mod;
p>>=1;
}
return ans;
}
void init(){
inv[0]=fac[0]=1;
for(int i=1;i<=N;i++) fac[i]=fac[i-1]*i%mod;
inv[N]=qpow(fac[N],mod-2);
for(int i=N-1;i>=0;i--) inv[i]=inv[i+1]*(i+1)%mod;
}
int C(int x,int y){
if(x<y) return 0;
return fac[x]*inv[y]%mod*inv[x-y]%mod;
}
int CC(int x,int y){
int ans=1;
for(int i=x-y+1;i<=x;i++) ans=ans*i%mod;
for(int i=1;i<=y;i++) ans=ans*qpow(i,mod-2)%mod;
return ans;
}
int G(int u,int a,int b){ // > : a // <= : b
int ans=0;
for(int x=1;x<=u;x++){
int w=qpow(u-x,a)*qpow(x,b)%mod;
ans=(ans+w)%mod;
}
return ans;
}
signed main(){
int n,m,k;
scanf("%lld%lld%lld",&n,&m,&k);
for(int i=1;i<=m;i++) scanf("%lld",&U[i]);
for(int i=1;i<=m;i++) scanf("%lld",&R[i]);
init();
// 比自己高的一定是不被碾压的,比自己低的不一定是被碾压的
for(int p=1;p<=n;p++){
g[p]=C(n-1,p); // 至少有 p 个被碾压的
for(int i=1;i<=m;i++){
g[p]=g[p]*C(n-p-1,R[i]-1)%mod;
}
}
int fx=0;
for(int i=k;i<=n;i++){
int tmp=((i-k)%2 ? -1 : 1);
fx=(fx+tmp*C(i,k)%mod*g[i]%mod+mod)%mod;
}
int fy=1;
for(int i=1;i<=m;i++){
int tp=min(n,U[i]);
for(int t=1;t<=tp;t++){
h[t]=G(t,R[i]-1,n-R[i])%mod;//*C(U[i],t)
}
int cnt=0;
for(int t=1;t<=tp;t++){
int sum=0;
for(int j=1;j<=t;j++){
int tmp=((t-j)%2 ? -1 : 1);
int kl=tmp*C(t,j)%mod*h[j]%mod;
sum=(sum+kl+mod)%mod;
}
cnt=(cnt+sum*CC(U[i],t)%mod+mod)%mod;
}
fy=fy*cnt%mod;
}
printf("%lld",fx*fy%mod);
}
情侣?给我烧了! #
设
设
我们考虑优化它
这样我们发现里面的东西只和
code
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2005;
const int mod=998244353;
int f[N+5],g[N+5];
int fac[N+5],inv[N+5];
int qpow(int x,int p){
int ans=1;
while(p){
if(p&1) ans=(ans*x)%mod;
x=(x*x)%mod;
p>>=1;
}
return ans;
}
void init(){
fac[0]=inv[0]=1;
for(int i=1;i<=N;i++) fac[i]=fac[i-1]*i%mod;
inv[N]=qpow(fac[N],mod-2);
for(int i=N-1;i>=1;i--) inv[i]=inv[i+1]*(i+1)%mod;
}
int C(int x,int y){
return fac[x]*inv[y]%mod*inv[x-y]%mod;
}
signed main(){
init();
for(int lim=0;lim<=N/2;lim++){
for(int i=0;i<=lim;i++){
int w=(i&1 ? -1 : 1);
int tmp=fac[lim-i]*fac[lim-i]%mod*fac[i]%mod;
g[lim]=(g[lim]+w*qpow(2,i)*fac[2*lim-2*i]%mod*qpow(tmp,mod-2)%mod+mod)%mod;
}
}
int T;
scanf("%lld",&T);
while(T--){
int n;
scanf("%lld",&n);
for(int k=0;k<=n;k++){
int w=qpow(2,k)*fac[n]%mod*fac[n]%mod*qpow(fac[k],mod-2)%mod;
int ans=w*g[n-k]%mod;
printf("%lld\n",ans);
}
}
}
多元二项式反演公式#
如果满足
那么就有
Sky Full of Stars #
设
那么有
发现答案其实是
考虑
-
假如都不为 0
-
有一维为 0
-
两维均为 0
所以我们要分开考虑,先考虑第一部分,我们合并同类项得:
问题在于
最后就是
然后再算一下第二部分,让一个是然后结果乘二就行
其实还可以化成二项式形式,也就是
那么就有
然后再算第三部分。
code
// Sky Full of Stars
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+5;
const int mod=998244353;
int fac[N+5],inv[N];
int qpow(int x,int p){
p=(p%(mod-1)+mod-1)%(mod-1);
x=(x%mod+mod)%mod;
int ans=1;
while(p){
if(p&1) ans=(ans*x)%mod;
x=(x*x)%mod;
p>>=1;
}
return ans;
}
void init(){
fac[0]=inv[0]=1;
for(int i=1;i<=N;i++) fac[i]=fac[i-1]*i%mod;
inv[N]=qpow(fac[N],mod-2);
for(int i=N-1;i>=1;i--) inv[i]=inv[i+1]*(i+1)%mod;
}
int C(int x,int y){
return fac[x]*inv[y]%mod*inv[x-y]%mod;
}
int base[N];
signed main(){
init();
int n;
scanf("%lld",&n);
int ans=0;
for(int i=1;i<=n;i++){
int w=(i%2 ? -1 : 1);
w=w*C(n,i)%mod*qpow(3,-i*n)%mod*(qpow(1-qpow(3,-n+i),n)-1+mod)%mod;
ans=(ans+w+mod)%mod;
}
ans=ans*qpow(3,n*n+1)%mod;
for(int i=1;i<=n;i++){
int w=(i%2 ? -1 : 1);
w=w*C(n,i)%mod*qpow(3,n*(n-i))%mod*qpow(3,i)%mod;
ans=(ans+w*2%mod+mod)%mod;
}
printf("%lld",(mod-ans)%mod);
}
min-max 容斥#
考虑每个
只有
最小公倍佩尔数#
考虑递推求出
可以得出
然后有
然后考虑求
那么有式子
因为
并且对于
我们令
我们考虑
一定有
然后就有
作者:bloss
出处:https://www.cnblogs.com/jinjiaqioi/p/17998452
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效