组合计数 总结

组合计数总结

 

递推求组合数  类似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 }
View Code

 也可以 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 }
View Code

 

 

用组合数学来解决

题目链接: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 }
View Code

 

 

 

题目链接: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 }
View Code

拆分成两个矩形 先枚举上面的矩形有多少个 然后排列组合公式求解

 

 

题目链接: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 }
View Code

用全部减去不合法的情况 首先 所有的点有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 }
View Code

 

posted @ 2021-05-01 19:49  canwinfor  阅读(30)  评论(0编辑  收藏  举报