[HNOI2017]抛硬币

Posted D O Time

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[HNOI2017]抛硬币相关的知识,希望对你有一定的参考价值。

标签:扩展卢卡斯+方案数推导。
题解:

  首先要想到使用组合数而不是DP,否则就会深陷泥潭而不可自拔了。
  我们把两个序列拼起来,也就是a+b个位置,每一个位置都是0或1。自然有2^(a+b)种方案。我们分a==b和a>b两种情况来讨论(我也不知道怎么想到这两种情况是不一样的):
  a==b:首先a的赢和输是对称的,如果b赢,反过来就是一种a赢的方案,当然在2^(a+b)种方案中,自然也会有这一种赢的方案,所以相当于÷2即可。但是还有平局的情况,记为S,是要减掉的。减了之后再除。推导如下:

$$\sum_{i=0}^{a}C_a^i * C_a^i = \sum_{i=0}^{a}C_a^i * C_a^{a-i} = C_{2a}^a$$

所以$ans=\frac{2^{a+b}-S}{2}$

  a>b:同样,输和赢在一定程度上也是对称的,如设a赢A次,b赢B次,则如果A<=B,a是不赢的,那么a-A>b-B,是赢的。但是如果A>B的话,对称之后的输和赢是不好判断的了,所以我们要找到对于a来说不管是否对称都能赢的方案即为S。推导如下:

j的范围:$$[a-(i+j)>b-i] \to [a-i-j>b-i] \to [j<a-b] \to [j<=a-b-1]$$

S:$$\sum_{i=0}^{b}(C_b^i * \sum_{j=1}^{a-b-1} C_a^{i+j}) = \sum_{i=0}^{b}\sum_{j=1}^{a-b-1}(C_b^{b-i} * C_a^{i+j}) = \sum_{j=1}^{a-b-1}C_{a+b}^{b+j}$$

所以$ans=\frac{2^{a+b}+S}{2}$

  然后是a和b都十分的巨大,可以使用扩展卢卡斯定理来求解组合数。对于2^(a+b),变成2^(a+b-1),相当于÷2。对于取模512的,在最后分子的2的个数与分母的2的个数相减时,分子少乘一个2,相当于÷2。对于取模5^9的,乘以2的逆元即可。然后这道题再使用中国剩余定理进行合并即可,可以先把要用的数算出来,比如2的逆元之类的,就不要再在程序中算了。

  下面有一个地方需要注意:“对于取模512的,在最后分子的2的个数与分母的2的个数相减时,分子少乘一个2”,虽说如此,但是分子分母的2的个数可能相同,所以会错误比如C(7,3)。我们发现$\sum_{j=1}^{a-b-1}C_{a+b}^{b+j}$是对称的,即j从1到a-b-1,那么b+j从b+1到a-1,(b+1+a-1)/2=(a+b)/2,所以是对称的,所以我们分奇数和偶数,对于中间那个组合数/2,其他的不除二,直接仅仅枚举一半即可,这样就不会出现C(7,3)=35*(2的逆元)这种尴尬的情况了。

  1 #include<cstdio>
  2 #include<iostream>
  3 #include<algorithm>
  4 #define RG register
  5 #define LL long long
  6 using namespace std;
  7 const LL MAXN=2000009,mod1=512,mod2=1953125,mod=1e9,t1=109,t2=1537323;
  8 int k,FLAG;
  9 LL a,b,X,Y,CNT,ans;
 10 LL v2[1005],v5[1005],pow2[1000],pow5[MAXN];
 11 LL pow(RG LL x,RG LL y,RG LL MOD)
 12 {
 13   RG LL res=1;
 14   while(y)
 15     {
 16       if(y&1) res=res*x%MOD;
 17       x=x*x%MOD; y>>=1;
 18     }
 19   return res;
 20 }
 21 LL exgcd(RG LL A,RG LL B)
 22 {
 23   if(A%B==0) { X=0,Y=1; return B; }
 24   RG LL res=exgcd(B,A%B);
 25   RG LL tmp=X;
 26   X=Y; Y=tmp-A/B*Y;
 27   return res;
 28 }
 29 LL getni(RG LL x,RG LL p)
 30 {
 31   exgcd(x,p); X%=p;
 32   while(X<0) X+=p;
 33   return X;
 34 }
 35 LL ni2(RG LL x)
 36 {
 37   if(x<=0)return 1;
 38   RG LL A=pow(pow2[mod1],x/mod1,mod1);
 39   A=A*pow2[x%mod1]%mod1;
 40   CNT+=x/2;
 41   A=A*ni2(x/2)%mod1;
 42   return A;
 43 }
 44 LL C2(LL n,LL m)
 45 {
 46   if(n<m)return 0;
 47   RG LL A,B,C,nin,nim,ninm;
 48   CNT=0; nin=ni2(n); A=CNT;
 49   CNT=0; nim=ni2(m); B=CNT;
 50   CNT=0; ninm=ni2(n-m); C=CNT;
 51   nim=getni(nim,mod1);
 52   ninm=getni(ninm,mod1);
 53   if(FLAG)
 54     return ((nin*nim%mod1)*ninm%mod1)*v2[(A-B-C-1)]%mod1;
 55   else
 56     return ((nin*nim%mod1)*ninm%mod1)*v2[(A-B-C)]%mod1;
 57 }
 58 LL ni5(RG LL x)
 59 {
 60   if(x<=0)return 1;
 61   RG LL A=pow(pow5[mod2],x/mod2,mod2);
 62   A=A*pow5[x%mod2]%mod2;
 63   CNT+=x/5;
 64   A=A*ni5(x/5)%mod2;
 65   return A;
 66 }
 67 LL C5(RG LL n,RG LL m)
 68 {
 69   if(n<m)return 0;
 70   RG LL A,B,C,nin,nim,ninm;
 71   CNT=0; nin=ni5(n); A=CNT;
 72   CNT=0; nim=ni5(m); B=CNT;
 73   CNT=0; ninm=ni5(n-m); C=CNT;
 74   nim=getni(nim,mod2);
 75   ninm=getni(ninm,mod2);
 76   return ((nin*nim%mod2)*ninm%mod2)*v5[(A-B-C)]%mod2;
 77 }
 78 LL C(LL n,LL m)
 79 {
 80   RG LL A=C2(n,m),B=C5(n,m);
 81   if(FLAG)B=B*976563%mod2;
 82   A=(A*mod2%mod)*t1%mod;
 83   B=(B*mod1%mod)*t2%mod;
 84   return (A+B)%mod;
 85 }
 86 void print(LL x,int len)
 87 {
 88   x%=mod; while(x<0)x+=mod;
 89   int gg[15]={0};
 90   for(int i=1;i<=len;i++){gg[i]=x%10;x/=10;}
 91   for(int i=len;i>=1;i--)printf("%d",gg[i]);
 92   puts("");
 93 }
 94 int main()
 95 {
 96   freopen("coin.in","r",stdin);
 97   freopen("coin.out","w",stdout);
 98   pow2[0]=pow5[0]=v2[0]=v5[0]=1;
 99   for(int i=1;i<=1000;i++) v2[i]=v2[i-1]*2%mod1,v5[i]=v5[i-1]*5%mod2;
100   for(int i=1;i<=mod1;i++) pow2[i]=pow2[i-1]*(i%2?i:1)%mod1;
101   for(int i=1;i<=mod2;i++) pow5[i]=pow5[i-1]*(i%5?i:1)%mod2;
102   while(scanf("%lld%lld%d",&a,&b,&k)!=EOF)
103     {
104       FLAG=1;
105       if(a==b)
106         print(pow(2,a+b-1,mod)-C(a+b,a),k);
107       else
108         {
109           ans=pow(2,a+b-1,mod)%mod;
110           if(((a+b)&1)==0)
111             {
112               FLAG=1;
113               ans+=C(a+b,(a+b)/2);
114             }
115           FLAG=0;
116           LL gg=(a+b)/2;
117           if(((a+b)&1)==0) gg--;
118           for(LL i=(b+a)/2+1;i<a;i++)
119             ans=(ans+C(a+b,i))%mod;
120           print(ans,k);
121         }
122     }
123   return 0;
124 }

以上是关于[HNOI2017]抛硬币的主要内容,如果未能解决你的问题,请参考以下文章

刷题BZOJ 4830 [Hnoi2017]抛硬币

[HNOI2017]抛硬币

Excel:在 n 次抛硬币中出现 h 个正面的概率

用偏硬币模拟抛硬币的程序

概率DP 2017 ICPC 乌鲁木齐 A Coins

c_cpp 用计算机产生的伪随机数来模拟抛硬币试验。假设抛10次硬币,每次抛硬币得到正面和反面是随机的。抛10次硬币构成一个事件。调用随机(2)返回一个二值结果。在主程序中反复调用函数TossCoin