• [bzoj4830][Hnoi2017]抛硬币——数论+拓展卢卡斯定理


    题目大意:

    先抛a次硬币,再抛b次硬币,求前a次正面朝上的次数比后b次正面朝上的次数多的情况数。

    思路:

    不难发现答案为(sum_{i=0}^{a}sum_{j=0}^{i-1}{achoose i} imes {bchoose j}),然后将b的情况全部反过来,并将a,b接成同一个序列,答案变成了(sum_{i=b+1}^{a+b}{a+bchoose i})
    然后用组合数对称的性质将(sum_{i=frac{a+b}{2}}^{a+b}{a+bchoose i})给化成(2^{a+b-1}),剩下来的部分项数不多,可以直接用拓展卢卡斯求。
    由于这个题目比较特别,所以在拓展卢卡斯的时候需要注意几项:
    1.因为(a+b)的奇偶性的不同,有一个组合数要除2,这个直接在计算的时候特判一下,中国剩余的时候两个模数的结果都除以2即可,如果模数本身是2的倍数,那么在运算过程中要少乘一个2。
    2.求阶乘要预处理出来。
    3.有点卡常。

    #include<bits/stdc++.h>
    
    #define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
    #define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i)
    #define MREP(i,x) for(int i=beg[x],v;v=to[i],i;i=las[i])
    #define debug(x) cout<<#x<<"="<<x<<endl
    #define fi first
    #define se second
    #define mk make_pair
    #define pb push_back
    typedef long long ll;
    
    using namespace std;
    
    void File(){
    	freopen("bzoj4830.in","r",stdin);
    	freopen("bzoj4830.out","w",stdout);
    }
    
    template<typename T>void read(T &_){
    	T __=0,mul=1; char ch=getchar();
    	while(!isdigit(ch)){
    		if(ch=='-')mul=-1;
    		ch=getchar();
    	}
    	while(isdigit(ch))__=(__<<1)+(__<<3)+(ch^'0'),ch=getchar();
    	_=__*mul;
    }
    
    const int maxn=2e6+10;
    ll fac2[maxn],fac5[maxn];
    
    namespace ex{
    	ll exgcd(ll a,ll b,ll &x,ll &y){
    		if(!b){x=1,y=0;return a;}
    		ll g=exgcd(b,a%b,x,y),tmp=x;
    		x=y,y=tmp-a/b*y;
    		return g;
    	}
    	ll inv(ll a,ll b){
    		ll x,y;
    		exgcd(a,b,x,y);
    		return (x%b+b)%b;
    	}
    }
    
    ll qpow(ll x,ll y,ll mod){
    	if(x==1)return 1;
    	if(x==mod-1)return y%2 ? -1 : 1;
    	ll ret=1; x%=mod;
    	while(y){
    		if(y&1)ret=ret*x%mod;
    		x=x*x%mod;
    		y>>=1;
    	}
    	return ret;
    }
    
    ll Count(ll x,ll y){
    	ll ret=0;
    	for(;x;x/=y)ret+=x/y;
    	return ret;
    }
    
    ll Fac(ll x,ll y,ll mod){
    	if(!x)return 1;
    	ll ret=(y==2 ? fac2[mod] : fac5[mod])%mod;
    	ret=qpow(ret,x/mod,mod);
    	ret=ret*(y==2 ? fac2[x%mod] : fac5[x%mod])%mod;
    	return ret*Fac(x/y,y,mod)%mod;
    }
    
    ll calc(ll x,ll y,ll k,bool flag2,bool flag5){
    	ll p[3]={(ll)pow(10,k),(ll)pow(2,k),(ll)pow(5,k)},b[3]={0,2,5},c[3],ret=0;
    	REP(i,1,2){
    		ll cnt=Count(x,b[i])-Count(y,b[i])-Count(x-y,b[i]);
    
    		if(i==1 && flag2 && cnt)flag2=0,--cnt;
    		ll mul=qpow(b[i],cnt,p[i]);
    		if(i==2 && flag5)flag5=0,mul=mul*ex::inv(2,p[i])%p[i];
    		mul=mul*Fac(x,b[i],p[i])%p[i];
    		ll inv;
    		inv=Fac(y,b[i],p[i])*Fac(x-y,b[i],p[i])%p[i];
    		inv=ex::inv(inv,p[i]);
    		c[i]=mul*inv%p[i];
    	}
    	REP(i,1,2)ret=(ret+p[3-i]*ex::inv(p[3-i],p[i])*c[i])%p[0];
    	return (ret+p[0])%p[0];
    }
    
    void work(){
    	ll a,b,k,ans,mod;
    	while(~scanf("%lld%lld%lld",&a,&b,&k)){
    		mod=pow(10,k); ans=qpow(2,a+b-1,mod);
    		for(ll i=b+1;i<=(a+b)/2;++i)
    			ans=(ans+calc(a+b,i,k,0,0))%mod;
    		if((a+b)%2==0)ans=(ans-calc(a+b,(a+b)/2,k,1,1))%mod;
    		ans=(ans+mod)%mod;
    		DREP(i,k,1)printf("%d",(int)ans/(int)pow(10,i-1)%10);
    		putchar('
    ');
    	}
    }
    
    void init(){
    	fac2[0]=fac5[0]=1;
    	ll mod2=pow(2,9),mod5=pow(5,9);
    	REP(i,1,2e6){
    		if(i%2)fac2[i]=fac2[i-1]*i%mod2;
    		else fac2[i]=fac2[i-1];
    		if(i%5)fac5[i]=fac5[i-1]*i%mod5;
    		else fac5[i]=fac5[i-1];
    	}
    }
    
    int main(){
    	File();
    	init();
    	work();
    	return 0;
    }
    
    
  • 相关阅读:
    a gcc 4.2.4 bug(被stos指令累加后%edi作为参数的)
    gcc -02引起内存溢出'unsigned i'应修订为'volatile unsigned i'
    gcc优化引起get_free_page比__get_free_page返回值多4096
    gcc请不要优化
    change_bit 按位取反
    IBM messed up *AGAIN* in their thinkpad: 0xA0000 -> 0x9F000
    python正则实例
    详解volatile 关键字与内存可见性
    并发基础知识
    Spring通过注释配置Bean2 关联关系
  • 原文地址:https://www.cnblogs.com/ylsoi/p/10097150.html
Copyright © 2020-2023  润新知