组合数+容斥原理

复制代码
//组合数 // O(n^2),预处理递推 //n---1~2e3 #include<bits/stdc++.h> using namespace std; const int N=1e3+10,mod=1e9+7; int n,m,c[N][N]; int main() { cin>>n; for(int i=0;i<=N;i++) for(int j=0;j<=i;j++) if(!j) c[i][j]=1; else c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod; //预处理 while(n--){ int a,b; cin>>a>>b; cout<<c[a][b]<<'\n'; } return 0; } //O(ologn),预处理阶乘 //https://blog.csdn.net/qq_44661312/article/details/103324485 //n---1~1e5 #include<bits/stdc++.h> #define int long long using namespace std; const int N=1e5+10,mod=1e9+7; int fact[N],_fact[N]; int n,m; int qpow(int a,int k,int p)//快速幂 { int res=1; while(k){ if(k&1) res=res*a%p; a=a*a%p; k>>=1; } return res; } signed main() { cin>>n; fact[0]=_fact[0]=1; for(int i=1;i<N;i++){ fact[i]=fact[i-1]*i%mod; _fact[i]=_fact[i-1]*qpow(i,mod-2,mod)%mod;//求阶乘的逆元 } while(n--){ int a,b; cin>>a>>b; cout<<fact[a]*_fact[b]%mod*_fact[a-b]%mod<<'\n'; } return 0; } //lucas定理(卢卡斯定理),O(logn*logp*p) //https://blog.csdn.net/Qiuker_jl/article/details/109528164?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522169260990816800188593900%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=169260990816800188593900&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduend~default-1-109528164-null-null.142^v93^chatgptT3_2&utm_term=%E5%8D%A2%E5%8D%A1%E6%96%AF%E5%AE%9A%E7%90%86%E8%AF%81%E6%98%8E&spm=1018.2226.3001.4187 //n---1~20,mod---1~1e5,a'b---1~1e18 #include<bits/stdc++.h> #define int long long using namespace std; int p,n; int qpow(int a,int k,int mod) { int res=1; while(k){ if(k&1) res=res*a%p; a=a*a%p,k*=2; } return res; } int C(int a,int b) { int res=1; for(int i=1,j=a;i<=b;i++,j--){ res=res*j%p; res=res*qpow(i,p-2,p)%p; } return res; } int lucas(int a,int b) { if(a<p&&b<p) return C(a,b); else return C(a%p,b%p)*lucas(a/p,b/p)%p; } signed main() { cin>>n; while(n--){ int a,b; cin>>a>>b>>p; cout<<lucas(a,b)<<'\n'; } return 0; }
复制代码

 线性乘法逆元递推关系式

inv[1]=1; for(int i=2;i<=n;i++) inv[i]=p-(p/i)*inv[p%i]%p;

 预处理阶乘逆元优化

复制代码
void init() { fac[0]=_fac[0]=1; for(int i=1;i<=200000;i++) fac[i]=fac[i-1]*i%mod; _fac[200000]=qpow(fac[200000],mod-2); for(int i=200000-1;i;i--) _fac[i]=_fac[i+1]*(i+1)%mod; }
复制代码

 

 

复制代码

//容斥原理 //时间复杂度O(2^n-1) #include<bits/stdc++.h> #define int long long using namespace std; const int N=2e6+10; int n,m,res,p[N]; signed main() { cin>>n>>m; for(int i=0;i<m;i++) cin>>p[i]; for(int i=1;i<1<<m;i++){//枚举2^n-1次,因为有这些种可能 int cnt=0,t=1; for(int j=0;j<m;j++){ if(i>>j&1){ cnt++; t*=p[j]; if(t>n) break; } } if(cnt%2) res+=n/t; //如果是奇数,就加上 else res-=n/t; } cout<<res<<endl; return 0; }
复制代码

 

 

 **基本概念:**

- |U|:所有情况的方案数
- Si:满足条件 i 的方案数
- ⋃:集合的并运算(表示满足任一条件的方案数)
- ⋂:集合的交运算(表示全部条件都满足的方案数)

**容斥原理公式:**

1. 求至少满足一个条件的方案数:

|i=1nSi|=m=1n(1)m11i1<i2<<imn|j=1mSij|

这个公式用于计算至少满足一个条件的方案数,其中 m 表示满足条件的条件数目,Sij 表示满足第 ij 个条件的方案集合。

2. 求恰好满足所有条件的方案数:

|i=1nSi|=|U||i=1nSi|

这个公式用于计算恰好满足所有条件的方案数。

容斥的核心思想是对“至少(至多)”和“恰好(一般是)”之间的转换,通过交和并的运算来得到不同情况下的方案数。

复制代码
//分特产 //https://www.luogu.com.cn/problem/P5505 //假设此时有i个人得不到第j个特产,则n-i个人分a[j]个特产,根据插板法可以得到 //方案数为 C(a[j]+n-i-1,n-i-1),而此时又要选择i个人,所以有C(n,i)个方法,根据乘法原理 //再使用容斥原理删除一些不合法的状态即可 #include<bits/stdc++.h> #define int long long using namespace std; const int N=5000,mod=1e9+7; int n,m,fac[N],_fac[N],k,a[N],res; int qpow(int a,int k) { int res=1; while(k){ if(k&1) res=res*a%mod; a=a*a%mod,k/=2; } return res; } int C(int a,int b) { return fac[a]*_fac[b]%mod*_fac[a-b]%mod; } signed main() { cin>>n>>m; fac[0]=_fac[0]=1,k=1; for(int i=1;i<N;i++) fac[i]=fac[i-1]*i%mod; _fac[N-1]=qpow(fac[N-1],mod-2); for(int i=N-2;i;i--) _fac[i]=(i+1)*_fac[i+1]%mod; for(int i=1;i<=m;i++) cin>>a[i]; for(int i=0;i<n;i++,k=-k){ int cnt=1; for(int j=1;j<=m;j++) cnt=cnt*C(a[j]+n-i-1,n-i-1)%mod; res=(res+C(n,i)*k%mod*cnt%mod+mod)%mod; } cout<<res; return 0; }
复制代码

 

一个 n × m 的棋盘,用 c 种颜色染色,求满足条件的方案数:

- 棋盘的每一个小方格既可以染色(染成 c 种颜色中的一种),也可以不染色。
- 棋盘的每一行至少有一个小方格被染色。
- 棋盘的每一列至少有一个小方格被染色。
- 每种颜色都在棋盘上出现至少一次。

 

 

复制代码
#include<bits/stdc++.h> #define int long long using namespace std; const int N=1000,mod=1e9+7; int n,m,fac[N],_fac[N],num,res,k,c; int f[N]; int qpow(int a,int k) { int num=1; while(k){ if(k&1) num=num*a%mod; a=a*a%mod,k/=2; } return num; } int C(int a,int b) { return fac[a]*_fac[b]%mod*_fac[a-b]%mod; } signed main() { cin>>n>>m>>c; fac[0]=_fac[0]=1; for(int i=1;i<N;i++) fac[i]=i*fac[i-1]%mod; _fac[N-1]=qpow(fac[N-1],mod-2); for(int i=N-2;i;i--) _fac[i]=(i+1)*_fac[i+1]%mod; for(int i=1;i<=c;i++){ int cnt=0; for(int k=1,q=1;k<=m;k++,q=-q) cnt=(cnt+C(m,k)%mod*qpow(qpow(i+1,m-k)-1,n)%mod*q+mod)%mod; f[i]=(qpow(qpow(i+1,m)-1,n)%mod-cnt+mod)%mod; } int num=0; for(int i=1,q=1;i<=c;i++,q=-q) num=(num+(C(c,i)*f[c-i]%mod*q+mod)%mod)%mod; res=(f[c]-num+mod)%mod; cout<<res<<endl; return 0; }
复制代码

 


__EOF__

本文作者Sakurajimamai
本文链接https://www.cnblogs.com/o-Sakurajimamai-o/p/17646813.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   o-Sakurajimamai-o  阅读(62)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
-- --
点击右上角即可分享
微信分享提示