组合计数 总结
组合计数总结
递推求组合数 类似dp
题目链接:https://www.acwing.com/problem/content/1309/
假设公牛为1 母牛为0 dp[i] 为以1结尾长度为i 的方案
所以划分子集的话就是 dp[i]=dp[1]+dp[2]+……dp[i-k-1] 但是这样状态计算的时候是n^2的所以需要用前缀和优化一下
那么最终答案就是 dp[0]+dp[1]+……dp[n] 以1最后出现的位置不重不漏的表示了所有的方案 即s[n] 就是最终答案
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define ll long long 4 #define pi pair<int,int> 5 #define pb push_back 6 #define fi first 7 #define sc second 8 #define ull unsigned long long 9 const int maxn=1e5+10; 10 const int mod=5000011; 11 12 13 int dp[maxn]; 14 int s[maxn]; 15 16 17 18 19 int main() 20 { 21 ios::sync_with_stdio(0); 22 cin.tie(0); 23 int n,k;cin>>n>>k; 24 25 dp[0]=s[0]=1; 26 for(int i=1;i<=n;i++) 27 { 28 dp[i]=s[max(0,i-k-1)]; 29 s[i]=s[i-1]+dp[i];s[i]%=mod; 30 } 31 cout<<s[n]<<'\n'; 32 33 34 35 36 37 }
也可以 dp[i][0/1] 前i个牛放好 第i个牛是母/公 牛的放法 类似状态机来做
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define ll long long 4 #define pi pair<int,int> 5 #define pb push_back 6 #define fi first 7 #define sc second 8 #define ull unsigned long long 9 const int maxn=1e5+10; 10 const int mod=5000011; 11 12 int dp[maxn][2]; 13 14 15 16 int main() 17 { 18 ios::sync_with_stdio(0); 19 cin.tie(0); 20 int n,k;cin>>n>>k; 21 dp[0][0]=1; 22 for(int i=1;i<=n;i++) 23 { 24 dp[i][0]=dp[i-1][0]+dp[i-1][1]; 25 dp[i][0]%=mod; 26 if(i-k-1>0) dp[i][1]=dp[i-k-1][0]+dp[i-k-1][1]; 27 else dp[i][1]=1; 28 } 29 cout<<(dp[n][1]+dp[n][0])%mod<<'\n'; 30 31 32 33 34 35 }
用组合数学来解决
题目链接:https://www.acwing.com/problem/content/1310/
将n 放成 n个1 中间有n-1个空隙 可以放k-1个隔板 这样就可以有顺序的划分
因为对于每一个正整数都可以 开始选择几个数 就是几 这样来映射过去
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define ll long long 4 #define pi pair<int,int> 5 #define pb push_back 6 #define fi first 7 #define sc second 8 #define ull unsigned long long 9 const int maxn=1e3+10; 10 const int mod=1e3; 11 12 13 int p[maxn],st[maxn],cnt[maxn],tot; 14 15 void getpr() 16 { 17 int n=maxn-1; 18 for(int i=2;i<=n;i++) 19 { 20 if(!st[i]) p[++tot]=i; 21 for(int j=1;p[j]*i<=n;j++) 22 { 23 st[p[j]*i]=1; 24 if(i%p[j]==0) break; 25 } 26 } 27 } 28 29 30 int power(int b,int n) 31 { 32 int r=1; 33 while(n) 34 { 35 if(n&1) r=r*b%mod; 36 b=b*b%mod; 37 n>>=1; 38 } 39 return r; 40 } 41 42 int get(int n,int p) 43 { 44 int res=0; 45 while(n) 46 { 47 res+=n/p; 48 n/=p; 49 } 50 return res; 51 } 52 53 vector<int>mul(vector<int>A,int b) 54 { 55 vector<int>C; 56 int t=0; 57 int la=A.size(); 58 for(int i=0;i<la||t;i++) 59 { 60 if(i<la) t+=A[i]*b; 61 C.pb(t%10); 62 t/=10; 63 } 64 while(C.size()>1&&C.back()==0) C.pop_back(); 65 return C; 66 } 67 68 69 70 int main() 71 { 72 ios::sync_with_stdio(0); 73 cin.tie(0); 74 int k,x; 75 cin>>k>>x; 76 int n=power(x%mod,x); 77 getpr(); 78 79 int a=n-1,b=k-1; 80 81 for(int i=1;i<=tot;i++) cnt[i]=get(a,p[i])-get(b,p[i])-get(a-b,p[i]); 82 83 vector<int>ans;ans.pb(1); 84 for(int i=1;i<=tot;i++) 85 { 86 for(int j=0;j<cnt[i];j++) 87 { 88 ans=mul(ans,p[i]); 89 } 90 } 91 for(int i=ans.size()-1;i>=0;i--) cout<<ans[i]; 92 93 94 95 96 97 98 99 }
题目链接:https://www.acwing.com/problem/content/1311/
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define ll long long 4 #define pi pair<int,int> 5 #define pb push_back 6 #define fi first 7 #define sc second 8 #define ull unsigned long long 9 const int maxn=2e3+10; 10 const int mod=1e5+3; 11 12 ll fac[maxn],inv[maxn]; 13 14 ll power(ll b,ll n) 15 { 16 ll r=1; 17 while(n) 18 { 19 if(n&1) r=r*b%mod; 20 b=b*b%mod; 21 n>>=1; 22 } 23 return r; 24 } 25 26 void init() 27 { 28 fac[0]=1; 29 for(int i=1;i<maxn;i++) fac[i]=fac[i-1]*i%mod; 30 inv[maxn-1]=power(fac[maxn-1],mod-2); 31 for(int i=maxn-2;i>=0;i--) 32 { 33 inv[i]=(inv[i+1]*(i+1))%mod; 34 } 35 } 36 37 ll C(ll n,ll m) 38 { 39 if(m>n) return 0; 40 return fac[n]*inv[m]%mod*inv[n-m]%mod; 41 } 42 ll A(ll n,ll m) 43 { 44 if(m>n) return 0; 45 return fac[n]*inv[n-m]%mod; 46 } 47 48 49 int main() 50 { 51 ios::sync_with_stdio(0); 52 cin.tie(0); 53 init(); 54 int a,b,c,d,k; 55 cin>>a>>b>>c>>d>>k; 56 ll ans=0; 57 for(int i=0;i<=k;i++) 58 { 59 ll tmp=C(b,i)*A(a,i)%mod*C(d,k-i)*A(a+c-i,k-i)%mod; 60 ans+=tmp; 61 ans%=mod; 62 } 63 cout<<ans<<'\n'; 64 65 66 67 68 }
拆分成两个矩形 先枚举上面的矩形有多少个 然后排列组合公式求解
题目链接:https://www.acwing.com/problem/content/1312/
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define ll long long 4 #define pi pair<int,int> 5 #define pb push_back 6 #define fi first 7 #define sc second 8 #define ull unsigned long long 9 const int maxn=2e3+10; 10 const int mod=1e5+3; 11 12 ll get(ll x) 13 { 14 return x*(x-1)*(x-2)/6; 15 } 16 17 18 int main() 19 { 20 ios::sync_with_stdio(0); 21 cin.tie(0); 22 int n,m; 23 cin>>n>>m; 24 n++;m++; 25 ll ans=get(n*m); 26 ans-=get(m)*n+get(n)*m; 27 28 m--;n--;//还原成长度 29 //枚举底为j 高为i的直角三角形 30 for(int i=1;i<=n;i++) 31 { 32 for(int j=1;j<=m;j++) 33 { 34 ans-=2ll*(__gcd(i,j)-1)*(n-i+1)*(m-j+1); 35 } 36 } 37 cout<<ans<<'\n'; 38 39 40 41 42 43 }
用全部减去不合法的情况 首先 所有的点有C(n*m,3) 然后 再考虑通过去除掉所有的同一条直线的不合法情况
分为斜率不存在 斜率为0 斜率大于/小于0 来算 斜率不存在和为0 的 就是 n*C(m,3) 即选取行 再选点 不存在的同理
那么再考虑斜率大于0的 因为小于0是对称的所以答案乘于2即可 只能够通过枚举底为j高为i的直角三角形来计算
因为要使用一个结论 gcd(i,j) +1 为包含起点和终点的所有在该直线上的点的数量
题目链接:https://www.acwing.com/problem/content/description/1314/
又是隔板法的应用 有两种思考方式
第一种简单一点的是 如果 要求的是 L<=ai<ai+1<ai+2……ak<=R 的方案数的话 那么其实只需要在 n个数中选取k个数即可
因为每个数都是不同的,选完之后自然就可以按照顺序排列出来 但是现在之间的数是可以相等的,那么可以让
a[1] 不变 a[2]+1 a[3]+2 a[k]+k-1 这样之后每个数之间的差就至少为1了 那么就可以使用上面的方法了
但是这样 右边界也从R 变成了R+k-1 所以 答案应该是 从R+k-1-L+1个数中 选取k个
但是最终答案 要的是 k从1到N的 所有的和 写出公式和 首加C(n+1,n+1) 尾减去C(n+1,n+1) 然后用组合数公式
可以从第一项一直合并到最后一项 即可得
第二种思考 将不等式 ai<=ai+1<=R 转换 令 xi=ai-ai-1 xi+1=ai+1-ai
以此得到x1+x2+……+xk<=R-L 令yi=xi+1 即可转换成正整数 y1+y2+yk <=R-L+k
然后隔板法 有R-L+k个空格, 其中放入k个板即可 即除了 第一个数前面的间隙不能放其他都能放, 最后一个板后面一段是丢弃不要的
隔板法只能应用与正整数 如果有0存在的话要考虑加上一些数 使得转换成正整数的存在
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define ll long long 4 #define pi pair<int,int> 5 #define pb push_back 6 #define fi first 7 #define sc second 8 #define ull unsigned long long 9 const int maxn=1e6+10; 10 const int mod=1e9+7; 11 12 int p=1e6+3; 13 14 ll fac[maxn],inv[maxn]; 15 16 ll power(ll b,ll n) 17 { 18 ll r=1; 19 while(n) 20 { 21 if(n&1) r=r*b%p; 22 b=b*b%p; 23 n>>=1; 24 } 25 return r; 26 } 27 28 void init() 29 { 30 fac[0]=1; 31 for(int i=1;i<p;i++) fac[i]=fac[i-1]*i%p; 32 inv[p-1]=power(fac[p-1],p-2); 33 for(int i=p-2;i>=0;i--) inv[i]=inv[i+1]*(i+1)%p; 34 } 35 36 ll C(ll n,ll m) 37 { 38 if(m>n) return 0; 39 return fac[n]*inv[m]%p*inv[n-m]%p; 40 } 41 ll lucas(ll n,ll m) 42 { 43 if(n<p&&m<p) return C(n,m); 44 return C(n%p,m%p)*lucas(n/p,m/p)%p; 45 } 46 47 48 49 int main() 50 { 51 ios::sync_with_stdio(0); 52 cin.tie(0); 53 int t; 54 cin>>t; 55 init(); 56 while(t--) 57 { 58 int n,l,r; 59 cin>>n>>l>>r; 60 cout<<(lucas(n+r-l+1,r-l+1)-1+p)%p<<'\n'; 61 } 62 63 64 65 66 67 68 69 }