[HNOI2017]抛硬币
Posted D O Time
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[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] \to [a-i-j>b-i] \to [j<a-b] \to [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 }
以上是关于[HNOI2017]抛硬币的主要内容,如果未能解决你的问题,请参考以下文章
c_cpp 用计算机产生的伪随机数来模拟抛硬币试验。假设抛10次硬币,每次抛硬币得到正面和反面是随机的。抛10次硬币构成一个事件。调用随机(2)返回一个二值结果。在主程序中反复调用函数TossCoin