[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 }
时间: 2024-11-07 02:04:53

[HNOI2017]抛硬币的相关文章

[AH/HNOI2017]抛硬币

题目描述 小 A 和小 B 是一对好朋友,他们经常一起愉快的玩耍.最近小 B 沉迷于**师手游,天天刷本,根本无心搞学习.但是已经入坑了几个月,却一次都没有抽到 SSR,让他非常怀疑人生.勤勉的小 A 为了劝说小 B 早日脱坑,认真学习,决定以抛硬币的形式让小 B 明白他是一个彻彻底底的非洲人,从而对这个游戏绝望.两个人同时抛 b 次硬币,如果小 A 的正面朝上的次数大于小 B 正面朝上的次数,则小 A 获胜. 但事实上,小 A 也曾经沉迷过拉拉游戏,而且他一次 UR 也没有抽到过,所以他对于自

【刷题】BZOJ 4830 [Hnoi2017]抛硬币

Description 小A和小B是一对好朋友,他们经常一起愉快的玩耍.最近小B沉迷于**师手游,天天刷本,根本无心搞学习.但是已经入坑了几个月,却一次都没有抽到SSR,让他非常怀疑人生.勤勉的小A为了劝说小B早日脱坑,认真学习,决定以抛硬币的形式让小B明白他是一个彻彻底底的非洲人,从而对这个游戏绝望.两个人同时抛b次硬币,如果小A的正面朝上的次数大于小B正面朝上的次数,则小A获胜.但事实上,小A也曾经沉迷过拉拉游戏,而且他一次UR也没有抽到过,所以他对于自己的运气也没有太大把握.所以他决定在小

抛硬币的两种思维方式

抛硬币是经典统计学中最基础的案例,无论是理论还是实验,都证明了50%这个概率,而且抛的次数越多,越接近这个值. 我们来看看一下这个问答:(以下对话例子来源于塔勒布的<黑天鹅>) A:假设硬币是公平的,因为每次抛出硬币得到正面与反面的可能性都是相同的.我把它抛出了99次,每次都正好得到正面.那么,我下一次得到反面的概率是多大? 回答者1(统计学家):毫无疑问,当然是50%,因为统计学中的样本独立性,不管你前面得到了什么结果,与下一次的结果都无关,所以可能性依然是50%. 好吧,对于学过统计的人,

csu 1009 抛硬币

C - 抛硬币 CSU - 1009 James得到了一堆有趣的硬币,于是决定用这些硬币跟朋友们玩个小游戏.在一个N行M列的表格上,每一个第i行第j列的格子上都放有一枚James的硬币,抛该硬币正面朝上的概率为Pij,所有抛硬币事件两两之间是相互独立的. 现在,玩家在M列硬币中,从每一列里各选择1枚,共M枚,构成一组.如此重复选择N组出来,且保证被选择过的硬币不能再选.选好组之后,每组的M枚硬币各抛一次,如果都是正面朝上,则该组胜利,总分赢得1分:否则该组失败,总分不加也不减.请问,如果让你自行

模拟抛硬币(C语言实现)

实现代码: 1 #include<stdio.h> 2 #include<stdlib.h> 3 4 int heads() 5 { 6 return rand() < RAND_MAX/2; 7 } 8 9 int main(int argc, char *argv[]) 10 { 11 int i,j,cnt; 12 int N = atoi(argv[1]), M = atoi(argv[2]); 13 int *f = malloc((N+1)*sizeof(int)

抛硬币 Flipping Coins(Gym - 101606F)

Here's a jolly and simple game: line up a row of N identical coins, all with the heads facing down onto the table and the tails upwards, and for exactly K times take one of the coins, toss it into the air, and replace it as it lands either heads-up o

抛硬币问题

每次抛掷硬币正面向上和反面向上的概率是相同的 问题 1 :抛掷硬币 n 次,求连续 k 次正面向上的方案数有多少种 ? 一个比较好想的点子是直接 2^n 枚举,在这其中寻找符合要求的有多少种,复杂度爆表... 在计算连续 k 次正面向上的方案数可能并不太好算,那么就转换成 用总的方案数减去仅有连续小于 k 次的方案数 dp[i] 表示 到第 i 个位置仅存在小于连续 k 次正面向上的方案数 1 . 当 i < k 时, dp[i] = dp[i-1]*2 2 . 当 i == k 时, dp[i

用随机数列模拟抛硬币

先粘贴上代码 package djbc; import java.util.Random;import java.util.Scanner; public class Lian {public static void main(String[] args) {int i=0,k=0,t=0;  System.out.println("请输入要抛的次数");Scanner scan=new Scanner(System.in);//输入抛硬币的次数t=scan.nextInt();Ran

HZOI20190908模拟40 队长快跑,影魔,抛硬币 题解

题面:https://www.cnblogs.com/Juve/articles/11487699.html 队长快跑: 权值线段树与dp yy的不错 #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; const int MAXN=1e5+5; int n,a[MAXN],b[