题解 P1492 【猩猩散步】

· · 题解

update on \sf 2021.12.22

以下定义 x\in Px 是质数。

这个题目让我们求 \binom{n+m}{m}\bmod 10^{100}

显然需要高精度计算。如果你使用朴素的高精,(n+m)! 可能会达到 10^5!,大概有 400000 多位。

这个 400000 多位是对它取对数得到的,\log_{10}(10^5!)\approx 456573.45

朴素高精乘法很慢,对于两个位数为 n 的数相乘,相当于暴力卷积,是 \mathcal{O(n^2)} 的,不能在本题限制内解决问题。

更重要的是,还要涉及到高精度除法。

所以我们要换一种做法。

我们把 [1,n+m] 中的每一个质数数找出来,然后考虑每一个质数出现了多少次。

我们知道 \binom{n+m}{m}=\frac{(n+m)!}{n!m!}

假设 p\in Pf_p=(n+m)! 的唯一分解中 p 的次数,a_p=n! 的唯一分解中 p 的次数,b_p=m! 的唯一分解中 p 的次数。

那么答案为 \prod\limits_{p\in P}p^{f_p-a_p-b_p}

记录 s_p=f_p-a_p-b_p,考虑求出 s

对于分子部分,每一个质因数的贡献是 +1,对于分母,则是 -1

我们知道 n 以内的质数数量 $\pi(n)\approx \frac{n}{\ln n}

记得一边做高精乘法一边取模,就是说,高精的位数不能超过 $100$ 位。 代码: ```cpp #include<cstdio> #include<algorithm> int n,m,l,maxn,t[100005],p[100005],cnt[100005]; long long ss[105]; void mul(int x) { for (int i=1;i<=l;i++) ss[i]=ss[i]*x; for (int i=1;i<=l||ss[i];i++) ss[i+1]=ss[i+1]+ss[i]/10,ss[i]=ss[i]%10,l=std::max(l,i); l=std::min(l,100); } void add(int x,int w) { while (x>1) { int d=t[x]; cnt[d]=cnt[d]+w; x=x/d; } } int main() { ss[l=1]=1; scanf("%d%d",&n,&m); maxn=n+m; for (int i=2;i<=maxn;i++) p[i]=1; for (int i=2;i<=maxn;i++) { if (!p[i]) continue; t[i]=i; for (int j=i,l=maxn/i;j<=l;j++) p[i*j]=0,t[i*j]=i; } for (int i=1;i<=n+m;i++) add(i,1); for (int i=1;i<=n;i++) add(i,-1); for (int i=1;i<=m;i++) add(i,-1); for (int k=1;k<=n+m;k++) for (int i=1;i<=cnt[k];i++) mul(k); for (int i=100;i>=1;i--) { printf("%lld",ss[i]); if (i%10==1) puts(""); } return 0; } ``` 实测时间 $\tt 77ms$,空间 $\tt 1.39MB$,代码长度 $\tt 873B$。 当然如果不限制高精位数也是可以通过的,$n=50000,m=50000$ 也能在 $\tt 1s$ 内跑完。 > 个人吐槽:本题解是我近 $3$ 年前写的,看到它成为本题最高赞的题解,我就修改一下排版与格式的问题。原来的存档在[这里](https://www.luogu.com.cn/paste/vxxgr15s)。感谢各位的阅读与支持。当时我很菜,啥题都不会做,做这个题目花了不少时间,但是我当时记得在哪里看到过这个做法,然后就做出来了,当时也挺高兴的。但是现在想想就不知有当时多么菜与浅薄了。