zoukankan      html  css  js  c++  java
  • [AH2017/HNOI2017]抛硬币

    传送门
    这个题的暴力比较好想……然后用一些组合的知识就可以变成正解了。
    首先我们考虑a=b的情况。我们把扔出来的硬币看成是一个01序列,那么对于一个b获胜的序列,他在每一位都按位异或1之后必然是一个a获胜的序列,那么a获胜的情况就是总情况减去平局,再除以二。总情况显然是(2^{a+b}),平局的我们能想到是(sum_{i=0}^a (C_a^i)^2),这个怎么快速计算……?一会会说到。
    之后考虑a > b的情况。现在任何一个a平局或者告负的局面,只要按位异或1之后都会转化为a胜的局面。因为a可以靠抛出硬币的数量取胜,那么首先我们枚举b抛出硬币的情况。之后对于a的情况,a抛出的硬币要比b多,那么我们可以枚举多出来的硬币数,结合起来就是(sum_{i=0}^bsum_{j=1}^{a-b-1} C_b^iC_a^{i+j}).
    这一部分的情况,即使将序列全部异或1,获胜的依然是a。那么这一部分的情况除以2,加上总的情况除以2即为答案。
    下面我们用到一个叫范德蒙德恒等式的东西,其形式如下:(sum_{i=0}^k C_n^iC_m^{k-i} = C_{n+m}^k)
    这个有两种证明方法。
    1.组合证明。
    我们考虑从(n+m)个元素中选取k个的情况数。我们分成两部分来看,一部分是从前n个里面取i个,一部分是从后面m个取k-i个,这两种之间用乘法原理乘起来。然后我们只要枚举每次取的情况,从0~k,就可以取遍所有情况,等式成立。
    2.生成函数证明。(也可以说是数学推导?233)
    ((1+x)^n(1+x)^m = (1+x)^{n+m})
    ((sum_{i=0}^nC_n^ix^i)(sum_{i=0}^mC_m^ix^i) = sum_{k=0}^{n+m}(sum_{i=0}^kC_n^iC_m^{k-i})x^k)(后面是卷积的形式)
    ((1+x)^{n+m} = sum_{k=0}^{n+m}C_{n+m}^kx^k)
    把这两个式子比较一下,之后就可以立即得到(sum_{i=0}^kC_n^iC_m^{k-i} = C_{n+m}^k)

    这时候我们就可以得到,平局的情况,应该是(C_{a+b}^a),而对于a>b时a获胜的情况,我们先把式子变形成这样:(sum_{i=1}^{a-b-1}sum_{j=0}^bC_b^{b-j}C_a^{i+j}),因为(b-j+i+j = b+i),所以式子可以看成(sum_{i=1}^{a-b-1}sum_{j+k = b+i}C_b^jC_a^k),根据上面的范德蒙德恒等式就可以得到答案为(sum_{i=1}^{a-b-1}C_{a+b}^{b+i})

    然后这时候你兴致冲冲写了扩展卢卡斯发现T成0分。
    这个题需要预先处理在模2和5的k次幂下的阶乘,为了方便扩展卢卡斯运算,需要中途把2和5的倍数直接抠掉。还有一个问题是怎么除以2.当a+b为奇数的时候,那么我们算的其实是杨辉三角中间的几列,只算一半就行。a+b为偶数的时候会剩下一项(C_{a+b}^{(a+b)/2}),这个我们用组合的知识,(C_{2a}^a = C_{2a-1}^a+C_{2a-1}^{a-1} = 2C_{2a-1}^a),计算它就可以了。

    #include<bits/stdc++.h>
    #define rep(i,a,n) for(ll i = a;i <= n;i++)
    #define per(i,n,a) for(ll i = n;i >= a;i--)
    #define enter putchar('
    ')
    
    using namespace std;
    typedef long long ll;
    const int M = 2000005;
    const int INF = 2147483647;
    const double eps = 1e-8;
    
    ll read()
    {
       ll ans = 0,op = 1;char ch = getchar();
       while(ch < '0' || ch > '9') {if(ch == '-') op = -1;ch = getchar();}
       while(ch >= '0' && ch <= '9') ans = ans * 10 + ch - '0',ch = getchar();
       return ans * op;
    }
    
    ll a,b,k,p,ans,fac[2][M],d1,d2;
    ll mul(ll a,ll b,ll t)
    {
       ll res = a * b - (ll)((long double)a / t * b + eps) * t;
       return (res % t + t) % t;
    }
    ll exgcd(ll a,ll b,ll &x,ll &y)
    {
       if(!b){x = 1,y = 0;return a;}
       ll d = exgcd(b,a%b,y,x);
       y -= a / b * x;
       return d;
    }
    ll inv(ll a,ll b)
    {
       ll x,y;
       exgcd(a,b,x,y);
       return (x % b + b) % b;
    }
    ll CRT(ll b,ll t) {return mul(mul(b,inv(p/t,t),p),p/t,p);}
    ll qpow(ll a,ll b,ll t)
    {
       ll p = 1;
       while(b)
       {
          if(b & 1) p = mul(p,a,t);
          a = mul(a,a,t),b >>= 1;
       }
       return p;
    }
    
    ll Fac(ll x,ll pi,ll pk)
    {
       if(!x) return 1;
       ll res = fac[pi!=2][pk];
       res = mul(qpow(res,x/pk,pk),fac[pi!=2][x%pk],pk);
       return mul(res,Fac(x/pi,pi,pk),pk);
    }
    
    ll C(ll n,ll m,ll pi,ll pk)
    {
       if(n < m) return 0;
       ll r = 0;
       for(ll i = n;i;i /= pi) r += i / pi;
       for(ll i = m;i;i /= pi) r -= i / pi;
       for(ll i = n-m;i;i /= pi) r -= i / pi;
       if(r >= k) return 0;//这一句不加会T
       ll g = Fac(n,pi,pk),g1= Fac(m,pi,pk),g2 = Fac(n-m,pi,pk);
       return mul(mul(g,inv(g1,pk),pk),mul(qpow(pi,r,pk),inv(g2,pk),pk),pk);
    }
    
    ll exlucas(ll n,ll m)
    {
       ll res = 0;
       res += CRT(C(n,m,2,d1),d1),res %= p;
       res += CRT(C(n,m,5,d2),d2),res %= p;
       return res;
    }
    
    void init(ll p,ll pk)
    {
       fac[p!=2][0] = 1;
       rep(i,1,pk) (i%p) ? fac[p!=2][i] = mul(fac[p!=2][i-1],i,pk) : fac[p!=2][i] = fac[p!=2][i-1];
    }
    
    int main()
    {
       //freopen("coin1.in","r",stdin);
       init(2,512),init(5,1953125);
       while(~scanf("%lld%lld%lld",&a,&b,&k))
       {
          p = qpow(10,k,INF),d1 = qpow(2,k,INF),d2 = qpow(5,k,INF);
          ans = qpow(2,a+b-1,p);
          if(a == b) ans += (p - exlucas((a<<1)-1,a)),ans %= p;
          else
          {
         rep(i,1,(a-b-1)>>1) ans += exlucas(a+b,b+i),ans %= p;
         if(!((a+b)&1)) ans += exlucas(a+b-1,(a+b)>>1),ans %= p;
          }
          while(ans < p / 10) putchar('0'),p /= 10;
          printf("%lld
    ",ans);
       }
       return 0;
    }
    
    
  • 相关阅读:
    C#调用C++编译的DLL--
    Visual studio 生成后事件说明
    页面缓存
    片段缓存
    数据缓存中的依赖关系
    四十条测试你是不是合格的PHP程序员
    YII的关联查询
    views中的公共代码放在一起
    html中出现的script失效
    Linux配置apache等系列
  • 原文地址:https://www.cnblogs.com/captain1/p/10394854.html
Copyright © 2011-2022 走看看