ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

gym103637F (2019-2020 10th BSUIR Open Programming Championship. Semifinal) 题解

2022-07-15 20:01:21  阅读:146  来源: 互联网

标签:Championship 题解 ki Programming ans 1i include ll mod


题意:从n个数中可重复地随机抽出m个数得到数列q,排序后q[d]>k则q合法, 求q合法的概率,需要输出d~n范围内的每个m对应的方案数。

 

不难得到对与固定的一个m (d<=m<=n),有P = Σd-1i=0C(m,i)ki(n-k)m-i  / n!  (i为抽到的不大于k的数的个数)。用这个式子计算需要O(d)的复杂度。

难点在于需要回答n-d+1个询问,如何快速计算呢?

 

定义ans[m] = Σd-1i=0C(m,i)ki(n-k)m-i  (P的分子,含义为对于固定的m,合法的q的数量)

ans[m+1]  =  Σd-1i=0C(m+1,i)ki(n-k)m+1-i  

因为C(m+1,i) = C(m,i) + C(m,i-1) (注意:特别地,当i等于0的时候,直接套公式有C(m+1,0) = C(m,0) + C(m,-1),此时可以认为C(m,-1) = 0,即有C(m+1,0) = C(m,0) = 1)

所以ans[m+1]  =  Σd-1i=0 (C(m,i) + C(m,i-1)) ki(n-k)m+1-i  = Σd-1i=0 C(m,i) ki(n-k)m+1-i  + Σd-1i=0 C(m,i-1) ki(n-k)m+1-i                                 (1)

观察这两项,

第一有Σd-1i=0 C(m,i) ki(n-k)m+1-i   = Σd-1i=0 C(m,i) ki(n-k)m-i * (n-k)  = ans[m] * (n-k) ;

第二有 Σd-1i=0 C(m,i-1) ki(n-k)m+1-i = Σd-1i=1 C(m,i-1) ki(n-k)m+1-i = Σd-2i=0 C(m,i) ki+1(n-k)m-i = k * (Σd-2i=0 C(m,i) ki(n-k)m-i ) = k * ( ans[m]-C(m,d-1) kd-1(n-k)m-d+1 )

(上式第一个等号是因为可以认为C(m,-1)=0;第二个等号是因为作了下标替换,实际上等号两边的项一一对应;

 

汇总上面两项,有递推式:ans[m+1] = ans[m] * (n-k)  +  k * ( ans[m]-C(m,d-1) kd-1(n-k)m-d+1 ) = n*ans[m]  + k*C(m,d-1) kd-1(n-k)m-d+1 

最后让所有ans都除以n!就得到概率,就可以ac本题了。

 

更重要的是,我们要分析上面式子的含义,不然感觉这题只是推个公式,太机械了。

上面的式子的重要基础是组合中的常用等式:C(m+1,i) = C(m,i) + C(m,i-1) ,含义为从m+1个数里选i个数的方案数,等于“必不选第一个数,从剩下的m个数里选i个数的方案数” + “必选第一个数,从剩下的m个数里选i-1个数的方案数” 。

式(1)等式右端的两项分别有以上含义,具体来说,

第一项为ans[m] * (n-k) ,表示m+1个数中的第一个数必选大于k的,然后再在剩下的m个数里选至多d-1个不大于k的数,以保证m+1个数中有至多d-1个不大于k。

第二项为k * ( ans[m]-C(m,d-1) kd-1(n-k)m-d+1 ) , 表示m+1个数中的第一个数必选不大于k的,然后再在剩下的m个数里选至多d-2个不大于k的数,以保证m+1个数中有至多d-1个不大于k。

 

AC代码:

 

 1 //exeCreate 2022-7-14 12:34:18    
 2 
 3 #include<stdio.h>
 4 #include<stdlib.h>
 5 #include<iostream>
 6 #include<algorithm>
 7 #include<string>
 8 #include<string.h>
 9 #include<cmath>
10 #include<vector>
11 #include<set>
12 #include<queue>
13 
14 #define rep(i,a,b) for(int i=(a);i<=(b);++i)
15 #define per(i,a,b) for(int i=(a);i>=(b);--i)
16 #define fi first
17 #define se second
18 #define mp make_pair
19 #define all(x) x.begin(),x.end()
20 
21 using namespace std;
22 typedef long long ll;
23 typedef pair<ll,ll> PII;
24 typedef pair<int,int> Pii;
25 const int maxn=3e5+10,maxm=maxn;
26 const ll mod=998244353,inf=0x3f3f3f3f;
27 
28 /*组合数*/
29 ll quickPow(ll a,ll b){
30     ll ret=1;
31     a%=mod;
32     while(b){
33         if(b&1) ret=ret*a%mod;
34         a=a*a%mod;
35         b>>=1;
36     }
37     return ret;
38 }
39 ll fac[maxn]={1},facinv[maxn];    //支持C(5000,2500)
40 void Cinit(){
41     rep(i,1,maxn-1) fac[i] = fac[i-1]*i%mod;
42     rep(i,0,maxn-1) facinv[i] = quickPow(fac[i],mod-2);
43 }
44 ll C(int n,int m){
45     return fac[n]*(facinv[n-m]*facinv[m]%mod)%mod;
46 }
47 
48 ll ans[maxn];
49 int main()
50 {
51     Cinit();
52     int n,d,k;    cin>>n>>d>>k;
53 
54     ans[d]=0;
55     int m=d;
56     rep(i,0,d-1){
57         ans[d] = (ans[d] + C(m,i)*quickPow(k,i)%mod*quickPow(n-k,m-i))%mod;
58     }
59 
60     rep(m,d,n-1){
61         ans[m+1] = (n*ans[m]%mod-C(m,d-1)*quickPow(k,d-1)%mod*quickPow(n-k,m-d+1)%mod*k%mod + mod)%mod;
62     }
63     
64     rep(i,d,n) {
65         ans[i] = ans[i]*quickPow(quickPow(n,i),mod-2)%mod;
66         printf("%lld\n",ans[i]);
67     }
68 }
69 //maxn改好了么?
View Code

 

这题挺不错的,对组合中的常用等式:C(m+1,i) = C(m,i) + C(m,i-1) 的运用比较好。

 

另外关于这场比赛(这场gym),A题的题解此处推荐一篇博客:https://www.cnblogs.com/iLex/p/16213006.html#_label0

标签:Championship,题解,ki,Programming,ans,1i,include,ll,mod
来源: https://www.cnblogs.com/laozhu1234/p/16482299.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有