zoukankan      html  css  js  c++  java
  • [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;
    }
    
    
  • 相关阅读:
    数据绑定表达式语法(Eval,Bind区别)
    使用博客园的第一件事 自定义主题
    sql2000 跨服务器复制表数据
    使用UpdatePanel 局部刷新出现中文乱码的解决方法!!
    MMC不能打开文件MSC文件
    sql 日期 、时间相关
    loaded AS2 swf call function in AS3 holder
    Rewrite the master page form action attribute in asp.net 2.0
    100万个不重复的8位的随机数
    flash 中实现斜切变型
  • 原文地址:https://www.cnblogs.com/ylsoi/p/10097150.html
Copyright © 2011-2022 走看看