HNOI2017 抛硬币

题解

实际上就是要求 a+bi=b+1Cia+b ,组合数取模即可。
直接算显然会超时,考虑优化。注意到 ni=0Cin=2n ,而且 Cin 是对称的。所以对于 a+b 为奇数的情况,直接用 2a+b/2=2a+b1 计算出 ni=a+b2Cia+b ,剩下 a+b2i=b+1Cia+b 可以直接暴力计算。
对于 a+b 为偶数的情况,需要特殊处理一下中间的边界。

SRC

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std ;

#define N 100 + 10
#define M 2000000 + 10
typedef long long ll ;

ll Tab[N] , f[2][M] ;
ll a , b , k , MO ;
int P = 1e9 ;
int ans ;

ll Power( ll x , ll k , int MO ) {
    ll s = 1 ;
    while ( k ) {
        if ( k & 1 ) s = s * x % MO ;
        x = x * x % MO ;
        k /= 2 ;
    }
    return s ;
}

ll Count( ll n , int p ) { return n == 0 ? 0 : n / p + Count( n / p , p ) ; }

ll fac( ll n , int p ) {
    if ( n == 0 ) return 1 ;
    ll ret = 1 ;
    int t = p == 2 ? 0 : 1 ;
    if ( n >= MO ) ret = f[t][MO] ;
    ret = Power( ret , n / MO , MO ) ;
    ret = ret * f[t][n%MO] % MO ;
    return ret * fac( n / p , p ) % MO ;
}

ll phi( ll p , ll pk ) { return pk / p * (p - 1) ; }

ll Calc( ll n , ll m ) {
    ll ret = 1 ;

    MO = Power( 2 , 9 , P ) ;
    ll k = Count( n , 2 ) - Count( m , 2 ) - Count( n - m , 2 ) ;
    ll a1 = 0 ;
    if ( k < 9 ) a1 = Power( 2 , k , MO ) * fac( n , 2 ) % MO * Power( fac( m , 2 ) * fac( n - m , 2 ) % MO , phi( 2 , MO ) - 1 , MO ) % MO ;
    ret = (P / MO) * Power( P / MO , phi( 2 , MO ) - 1 , MO ) % P * a1 % P ;

    MO = Power( 5 , 9 , P ) ;
    k = Count( n , 5 ) - Count( m , 5 ) - Count( n - m , 5 ) ;
    ll a2 = 0 ;
    if ( k < 9 ) a2 = Power( 5 , k , MO ) * fac( n , 5 ) % MO * Power( fac( m , 5 ) * fac( n - m , 5 ) % MO , phi( 5 , MO ) - 1 , MO ) % MO ;
    ret = (ret + (P / MO) * Power( P / MO , phi( 5 , MO ) - 1 , MO ) % P * a2 % P) % P ;

    return ret ;
}

ll Calc2( ll n , ll m ) {
    ll ret = 1 ;

    MO = Power( 2 , 9 , P ) ;
    ll k = Count( n , 2 ) - Count( m , 2 ) - Count( n - m , 2 ) - 1 ;
    ll a1 = 0 ;
    if ( k < 9 ) a1 = Power( 2 , k , MO ) * fac( n , 2 ) % MO * Power( fac( m , 2 ) * fac( n - m , 2 ) % MO , phi( 2 , MO ) - 1 , MO ) % MO ;
    ret = (P / MO) * Power( P / MO , phi( 2 , MO ) - 1 , MO ) % P * a1 % P ;

    MO = Power( 5 , 9 , P ) ;
    k = Count( n , 5 ) - Count( m , 5 ) - Count( n - m , 5 ) ;
    ll a2 = 0 ;
    if ( k < 9 ) a2 = Power( 5 , k , MO ) * fac( n , 5 ) % MO * Power( fac( m , 5 ) * fac( n - m , 5 ) % MO , phi( 5 , MO ) - 1 , MO ) % MO ;
    a2 = a2 * Power( 2 , phi( 5 , MO ) - 1 , MO ) % MO ;
    ret = (ret + (P / MO) * Power( P / MO , phi( 5 , MO ) - 1 , MO ) % P * a2 % P) % P ;

    return ret ;
}

int main() {
    freopen( "coin.in" , "r" , stdin ) ;
    freopen( "coin.out" , "w" , stdout ) ;
    Tab[0] = 1 ;
    for (int i = 1 ; i <= 9 ; i ++ ) Tab[i] = Tab[i-1] * 10 ;

    f[0][0] = f[1][0] = 1 ;
    int UP = Power( 2 , 9 , P ) ;
    for (int i = 1 ; i <= UP ; i ++ ) {
        f[0][i] = f[0][i-1] ;
        if ( i & 1 ) f[0][i] = f[0][i] * i % UP ;
    }

    UP = Power( 5 , 9 , P ) ;
    for (int i = 1 ; i <= UP ; i ++ ) {
        f[1][i] = f[1][i-1] ;
        if ( i % 5 ) f[1][i] = f[1][i] * i % UP ;
    }

    while ( scanf( "%lld%lld%lld" , &a , &b , &k ) != EOF ) {
        ans = Power( 2 , a + b - 1 , P ) ;
        if ( (a + b) & 1 ) {
            for (ll i = b + 1 ; i <= (a + b) / 2 ; i ++ ) ans = (ans + Calc( a + b , i )) % P ;
        } else {
            ans = ((ans - Calc2( a + b , (a + b) / 2 )) % P + P) % P ;
            for (ll i = b + 1 ; i <= (a + b) / 2 ; i ++ ) ans = (ans + Calc( a + b , i )) % P ;
        }
        ans %= Tab[k] ;
        if ( k == 1 ) printf( "%d\n" , ans ) ;
        if ( k == 2 ) printf( "%02d\n" , ans ) ;
        if ( k == 3 ) printf( "%03d\n" , ans ) ;
        if ( k == 4 ) printf( "%04d\n" , ans ) ;
        if ( k == 5 ) printf( "%05d\n" , ans ) ;
        if ( k == 6 ) printf( "%06d\n" , ans ) ;
        if ( k == 7 ) printf( "%07d\n" , ans ) ;
        if ( k == 8 ) printf( "%08d\n" , ans ) ;
        if ( k == 9 ) printf( "%09d\n" , ans ) ;
    }
    return 0 ;
}

以上.

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值