[AH2017/HNOI2017]抛硬币(扩展lucas)
推式子+exlucas。
题意:
小 A 和小 B 是一对好朋友,两个人同时抛 b 次硬币,如果小 A 的正面朝上的次数大于小 B 正面朝上的次数,则小 A 获胜。
小 A 决定在小 B 没注意的时候作弊,悄悄地多抛几次硬币,当然,为了不让小 B 怀疑,他不会抛太多次。
现在小 A 想问你,在多少种可能的情况下,他能够胜过小 B 呢?由于答案可能太大,所以你只需要输出答案在十进制表示下的最后 k 位即可。
有多组数据,对于每组数据输入三个数a,b,k,分别代表小A抛硬币的次数,小B抛硬币的次数,以及最终答案保留多少位整数。
\(a,b\leq10^{15}\),\(b\leq a\leq b+10000\),\(k\leq9\)。
看起来不太好做,但注意到\(b\leq a\leq b+10000\)。即\(0\leq a-b \leq 10000\)。
考虑两种情况:
1、在小A投b次时,就胜过小B。
先考虑前b个:总数为\(2^{b+b}\),前b个相等的有\(C(b,i)^2\)之和,即\(C(b+b,b)\)。
由于大于和小于等价,所以方案数为\(({2^{b+b}}-C(b+b,b))*2^{a-b}/2\)。
由于\(C(b+b,b)=C(b+b-1,b)+C(b+b-1,b-1)=2C(b+b-1,b)\),所以原式=\(({2^{b+b}}-C(b+b-1,b))*2^{a-b}\)。
2、在小A投b次时,没有胜过小B。
设小A前b次有x次,后a-b次有z次,小B有y次。则\(x+z>y\),即\(y-x<z\)。设\(i=y-x,z>i\)。
枚举i,则前b个方案数为\(C(b,x)*C(b,x+i)\)之和,即\(C(b,b-x)*C(b,x+i)\)之和,等于\(C(b+b,b+i)\)。
后a-b个方案数为\(C(a-b,z)(z>i)\)之和,可以枚举时维护后缀和。
但是,模数为\(10^k\),不是质数,很讨厌,需要exlucas。
首先,计算出\(C(n,m)mod 2^k\),\(C(n,m)mod 5^k\),通过中国剩余定理,可以算出\(C(n,m)mod 10^k\)。
以\(5^k\)为例:
因为\(5^k\)不是质数,不能lucas,需要用\(C(n,m)=n!/(m!*(n-m)!)\)计算。
然而,由于m很大时,\(m! mod 5^k=0\),无法求逆元。
所以,把\(n!,m!,(n-m)!\)中的5都拆出来,即化成\(a*5^b\)的性质。
暴力拆:例如\(k=2\),\(n=60\)。
\(60!=1*2*3*4*5*6*7*8*9*10*11*12*13*14*15*16*17*18*19*20*21*22*23*24*25*26*27*28*29*30*31*32*33*34*35*36*37*38*39*40*41*42*43*44*45*46*47*48*49*50*51*52*53*54*55*56*57*58*59*60\)。
把5提出,
\(60!=1*2*3*4*6*7*8*9*11*12*13*14*16*17*18*19*21*22*23*24*26*27*28*29*31*32*33*34*36*37*38*39*41*42*43*44*46*47*48*49*51*52*53*54*56*57*58*59*12!*5^{12}\)。
12!可以递归,考虑前面一堆:
我们发现,在mod25意义下,
\(1*2*3*4*6*7*8*9*11*12*13*14*16*17*18*19*21*22*23*24=26*27*28*29*31*32*33*34*36*37*38*39*41*42*43*44*46*47*48*49\)。
就是mod25后,有一堆循环节,所以只要算出\(1*2*3*4*6*7*8*9*11*12*13*14*16*17*18*19*21*22*23*24\),然后快速幂即可。
后面还有零散的\(51*52*53*54*56*57*58*59\),暴力算即可。
但是,这个算法需要\(5^9*10000*10\)次计算,显然超时。
考虑预处理:
首先,\(1*2*3*4*6*7*8*9*11*12*13*14*16*17*18*19*21*22*23*24\)可以预处理出。
其次,零散的\(51*52*53*54*56*57*58*59\)是一个前缀积,也可以预处理出。
这样,一次exlucas就是\(O(log^2n)\)的了(有快速幂的log)。
然而还是超时,考虑剪枝:
对于\(n!=a*x^b\),其中b可以很快算出。所以\(C(n,m)=a*x^b\),求出b如果大于等于k,则为0。
这样就很快了。
代码:
#include <stdio.h>
#define ll long long
int md;
int ksm(int a, ll b) {
int jg = 1;
while (b > 0) {
if (b & 1) jg = 1ll * jg * a % md;
a = 1ll * a * a % md;
b = (b >> 1);
}
return jg;
}
int j2[520],j5[1953130],m2,m5,m21,m51;
void dfs2(ll n, int & a, ll & b, int xk) {
if (n == 0) return;
int ta = 1;
ll tb = 0;
dfs2(n >> 1, ta, tb, xk);
a = 1ll * a * ta % md;
b += tb + (n >> 1);
if (n >= xk) a = 1ll * a * ksm(j2[xk - 1], n / xk) % md;
a = 1ll * a * j2[n - (n / xk) * xk] % md;
}
void dfs5(ll n, int & a, ll & b, int xk) {
if (n == 0) return;
int ta = 1;
ll tb = 0;
dfs5(n / 5, ta, tb, xk);
a = 1ll * a * ta % md;
b += tb + n / 5;
if (n >= xk) a = 1ll * a * ksm(j5[xk - 1], n / xk) % md;
a = 1ll * a * j5[n - (n / xk) * xk] % md;
}
ll jisuan(ll tn, int x) {
ll ms = 0;
while (tn > 0) {
ms += tn / x;
tn /= x;
}
return ms;
}
int getC2(ll n, ll m, int k) {
if (jisuan(n, 2) - jisuan(m, 2) - jisuan(n - m, 2) >= k) return 0;
int a1 = 1, a2 = 1, a3 = 1;
ll b1 = 0, b2 = 0, b3 = 0;
dfs2(n, a1, b1, m2);
dfs2(m, a2, b2, m2);
dfs2(n - m, a3, b3, m2);
b1 -= (b2 + b3);
a1 = 1ll * a1 * ksm(1ll * a2 * a3 % md, m21 - 1) % md;
return 1ll * a1 * ksm(2, b1) % m2;
}
int getC5(ll n, ll m, int k) {
if (jisuan(n, 5) - jisuan(m, 5) - jisuan(n - m, 5) >= k) return 0;
int a1 = 1, a2 = 1, a3 = 1;
ll b1 = 0, b2 = 0, b3 = 0;
dfs5(n, a1, b1, m5);
dfs5(m, a2, b2, m5);
dfs5(n - m, a3, b3, m5);
b1 -= (b2 + b3);
a1 = 1ll * a1 * ksm(1ll * a2 * a3 % md, m51 - 1) % md;
return 1ll * a1 * ksm(5, b1) % m5;
}
int c2,c5;
int C(ll n, ll m, int k) {
if (m < 0 || m > n) return 0;
int z2 = getC2(n, m, k);
int z5 = getC5(n, m, k);
return (1ll * z2 * c2 + 1ll * z5 * c5) % md;
}
int main() {
ll a,b;
int k,
lk = -1;
while (scanf("%lld%lld%d", &a, &b, &k) == 3) {
if (k != lk) {
md = 1;
for (int i = 0; i < k; i++) md *= 10;
m2 = ksm(2, k);
m5 = ksm(5, k);
m21 = ksm(2, k - 1);
m51 = 4 * ksm(5, k - 1);
c2 = 1ll * m5 * ksm(m5 % m2, m21 - 1) % md;
c5 = 1ll * m2 * ksm(m2, m51 - 1) % md;
j2[0] = j5[0] = 1;
for (int i = 1; i < m2; i++) {
j2[i] = j2[i - 1];
if (i % 2 != 0) j2[i] = 1ll * j2[i] * i % md;
}
for (int i = 1; i < m5; i++) {
j5[i] = j5[i - 1];
if (i % 5 != 0) j5[i] = 1ll * j5[i] * i % md;
}
}
int ans = 1ll * (ksm(2, b + b - 1) - C(b + b - 1, b, k) + md) % md * ksm(2, a - b) % md;
for (int i = a - b - 1, h = 0; i >= 0; i--) {
h = (h + C(a - b, i + 1, k)) % md;
ans = (ans + 1ll * C(b + b, b + i, k) * h) % md;
}
char zf[20];
sprintf(zf, "%d", ans + md);
printf("%s\n", zf + 1);
lk = k;
}
return 0;
}
[AH2017/HNOI2017]抛硬币(扩展lucas)的更多相关文章
- 洛谷P3726 [AH2017/HNOI2017]抛硬币(组合数+扩展Lucas)
题面 传送门 题解 果然--扩展\(Lucas\)学了跟没学一样-- 我们先考虑\(a=b\)的情况,这种情况下每一个\(A\)胜的方案中\(A\)和\(B\)的所有位上一起取反一定是一个\(A\)败 ...
- luogu P3726 [AH2017/HNOI2017]抛硬币
传送门 我是真的弱,看题解都写了半天,,, 这题答案应该是\(\sum_{i=1}^{a}\binom{a}{i}\sum_{j=0}^{min(b,i-1)}\binom{b}{j}\) 上面那个式 ...
- [AH2017/HNOI2017]抛硬币
传送门 这个题的暴力比较好想--然后用一些组合的知识就可以变成正解了. 首先我们考虑a=b的情况.我们把扔出来的硬币看成是一个01序列,那么对于一个b获胜的序列,他在每一位都按位异或1之后必然是一个a ...
- 题解 洛谷 P3726 【[AH2017/HNOI2017]抛硬币】
可以分别枚举两人正面朝上的次数来统计答案,所求即为 \[\sum_{i=0}^{a}\sum_{j=0}^{b} \binom{a}{i} \binom{b}{j} [i>j] \] 将\(i\ ...
- bzoj 4830: [Hnoi2017]抛硬币 [范德蒙德卷积 扩展lucas]
4830: [Hnoi2017]抛硬币 题意:A投a次硬币,B投b次硬币,a比b正面朝上次数多的方案数,模\(10^k\). \(b \le a \le b+10000 \le 10^{15}, k ...
- 【BZOJ4830】[HNOI2017]抛硬币(组合计数,拓展卢卡斯定理)
[BZOJ4830][HNOI2017]抛硬币(组合计数,拓展卢卡斯定理) 题面 BZOJ 洛谷 题解 暴力是啥? 枚举\(A\)的次数和\(B\)的次数,然后直接组合数算就好了:\(\display ...
- BZOJ4830 [Hnoi2017]抛硬币 【扩展Lucas】
题目链接 BZOJ4830 题解 当\(a = b\)时,我们把他们投掷硬币的结果表示成二进制,发现,当\(A\)输给\(B\)时,将二进制反转一下\(A\)就赢了\(B\) 还要除去平局的情况,最后 ...
- [luogu3726 HNOI2017] 抛硬币 (拓展lucas)
传送门 数学真的太优秀了Orz 数据真的太优秀了Orz 题目描述 小 A 和小 B 是一对好朋友,他们经常一起愉快的玩耍.最近小 B 沉迷于**师手游,天天刷本,根本无心搞学习.但是已经入坑了几个月, ...
- bzoj 4830: [Hnoi2017]抛硬币
Description 小A和小B是一对好朋友,他们经常一起愉快的玩耍.最近小B沉迷于**师手游,天天刷本,根本无心搞学习.但是 已经入坑了几个月,却一次都没有抽到SSR,让他非常怀疑人生.勤勉的小A ...
随机推荐
- 给内部类对象数组属性赋值时报错:Exception in thread "main" java.lang.NullPointerException
前言 1255: 打怪升级(Java),写这个题目程序的时候,控制台提示如下错误: Exception in thread "main" java.lang.NullPointer ...
- BZOJ4556 HEOI2016/TJOI2016字符串 (后缀树+主席树)
二分答案后相当于判断一个区间的后缀与某个后缀的最长公共前缀是否能>=ans.建出后缀树,在上述问题中后者所在节点向上倍增的跳至len>=ans的最高点,然后相当于查询子树中是否有该区间的节 ...
- 方法引用(method reference)
目录 方法引用(method reference) 1. 含义 2. 分类 3. 总结 方法引用(method reference) 1. 含义 方法引用实际上是 Lambda 表达式的一种语法糖. ...
- Nginx Too many open files
2019/07/25 08:31:31 [crit] 15929#15929: accept4() failed (24: Too many open files) 2019/07/25 08:31: ...
- opencv中自适应阈值函数的实现(c++)
根据<面向飞机蒙皮接缝的线结构光检测技术研究_张卡>论文中的原理,编写了自适应阈值函数 原理: //计算灰度最大最小值 void MaxGrayValue(Mat image,int &a ...
- 关于__new__和__init__
关于__new__和__init__ 例如一个类 class Foo(object): def __init__(self): print(1) def __new__(self): print(2) ...
- Python多个装饰器的顺序 转载
3.使用两个装饰器当一个装饰器不够用的话,我们就可以用两个装饰器,当然理解起来也就更复杂了,当使用两个装饰器的话,首先将函数与内层装饰器结合然后在与外层装饰器相结合,要理解@语法的时候到底执行了什么, ...
- g++ 生成C++ .so库文件,并调用示例
Tags: g++ C++ so library 在Linux系统下用g++命令编译C++程序.也可以生成so,a链接库 示例一 编译时链接so库 Test.h 文件内容 Main.cpp ...
- 03 Django之视图函数
一.Django的视图函数view 一个视图函数(类),简称视图,是一个简单的Python函数(类),它接受WEB请求并返回Web响应. 响应可以是一张网页的HTML内容,一个重定向,一个404错误, ...
- stm32 窗口看门狗 WWDG
窗口看门狗WWDG其实和独立看门狗类似,它是一个7位递减计数器不断的往下递减计数,当减到一个固定值0x40时还不喂狗的话,产生一个MCU复位,这个值叫窗口的下限,是固定的值,不能改变 窗口看门狗(WW ...