zoukankan      html  css  js  c++  java
  • [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] o [a-i-j>b-i] o [j<a-b] o [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 }
  • 相关阅读:
    20191218《信息安全导论》第十二周总结
    20191218《信息安全导论》第十一周总结
    20191218《信息安全导论》第十周总结
    20191218《信息安全导论》第九周总结
    20191218《信息安全导论》第八周总结
    20191218《信息安全导论》第七周总结
    20191218《信息安全导论》第六周总结
    20191218《信息安全导论》寻找我的黑客偶像
    20191218《信息安全导论》第五周总结
    20191211_获奖感言及学习心得
  • 原文地址:https://www.cnblogs.com/D-O-Time/p/7986404.html
Copyright © 2011-2022 走看看