同余系基本定理2

command_block

2020-04-04 12:57:04

Personal

上回请看 [同余系基本定理1](https://www.luogu.com.cn/blog/command-block/tong-yu-xi1) 默认大家已经对同余系有一定的了解,而且能够熟练运用基础算法。 - ## [P1495 【模板】中国剩余定理(CRT)](https://www.luogu.com.cn/problem/P1495) 有若干个同余方程$\begin{cases}x=c_1\pmod {p_1}\\x=c_2\pmod {p_2}\\...\\x=c_m\pmod {p_m}\end{cases}$ 满足$p_{1...m}$两两互质,求$x$的最小正整数解。 考虑把这些方程两两**合并**。 先看两个方程$\begin{cases}x=c_1\pmod{p_1}\\x=c_2\pmod {p_2}\end{cases}$ 可以构造$x=c_1p_2+c_2p_1$,这样在模$p_1$时显出$c_1p_2$,模$p_2$时显出$c_2p_1$. 问题在于,我们希望得到$c_1$而非$c_1p_2$。这好办,再乘上$p_2$在模$p_1$时的逆即可。 合并完之后的模数是$p_1p_2$,由于互质所以总能求逆。 边界方程可以视作$x=0\pmod 1$. 复杂度$O(m\log p)$. ```cpp #include<cstdio> #define ll long long using namespace std; void exgcd(ll a,ll b,ll &x,ll &y){ if (b==0){x=1;y=0;return ;} exgcd(b,a%b,y,x);y-=(a/b)*x; } ll inv(ll a,ll m){ ll x,y;exgcd(a,m,x,y); return (x%m+m)%m; } int n; ll c,p,ret,mp; int main() { scanf("%d",&n); ret=0;mp=1; for (int i=1;i<=n;i++){ scanf("%lld%lld",&p,&c); ll sp=mp*p; ret=(ret*inv(p,mp)%sp*p+mp*inv(mp,p)%sp*c)%sp; mp=sp; }printf("%lld",ret); return 0; } ``` - **例题** : [P3868 [TJOI2009]猜数字](https://www.luogu.com.cn/problem/P3868) - ## [P3807 【模板】卢卡斯定理](https://www.luogu.com.cn/problem/P3807) $\color{blue}\text{定理:}$ 当$p$是**素数**时 $$\dbinom{n}{m}\bmod p=\dbinom{\lfloor n/p\rfloor}{\lfloor m/p\rfloor}\dbinom{n\bmod p}{m\bmod p}\bmod p$$ $\color{blue}\text{证明:}$ (可不掌握) 注意到$[x^m](1+x)^n=\dbinom{n}{m}$ 构造$(1+x)^p=1+\binom{p}{1}x+\binom{p}{2}x+...+\binom{p}{p}x^p$ - 对于$1\leq i\leq p-1$,总有$\binom{p}{i}=0\pmod p$. 原因是$\dbinom{p}{i}=\dfrac{p!}{(p-i)!i!}$,而$p$是素数,所以 $(p-i)!$ 和 $i!$ 都不含因子$p$,无法抵消$p!$中的恰一个因子$p$. 那么可以得到$(1+x)^p=1+x^p\pmod p$ 能进一步推出$(1+x)^{p^k}=(1+x^p)^{p^{k-1}}=...=1+x^{p^k}$ 我们把$n,m$都表示成两个**p进制数**,即$\begin{cases}n=\sum\limits_{i=0}a_ip^i\\m=\sum\limits_{i=0}b_ip^i\end{cases}$ 然后考虑$(1+x)^n=\prod\limits_{i=0}(1+x)^{a_ip^i}$ $(1+x)^n=\prod\limits_{i=0}(1+x^{p^k})^{a^i}\pmod p$ 提取第$m$项系数。 $[x^m](1+x)^n=[x^m]\prod\limits_{i=0}(1+x^{p^k})^{a^i}\pmod p$ 由于乘积项的次数**不交**,可以把$m$分解。 “不交”的意思是某两位之间不会互相影响。因为将一个 $p$ 进制数的后 $n$ 位加起来之后肯定不足第 $n+1$ 位的一个 $1$ 大。 $\dbinom{n}{m}=\prod\limits_{i=0}[x^{b_ip^k}](1+x^{p^k})^{a^i}\pmod p$ $\dbinom{n}{m}=\prod\limits_{i=0}[x^{b_i}](1+x)^{a^i}\pmod p$ $\dbinom{n}{m}=\prod\limits_{i=0}\dbinom{a_i}{b_i}\pmod p$ 即 : 把 $n,m$ 按照 $p$ 进制拆位,每一位对应求组合数乘积即可。 ```cpp #include<cstdio> #define MaxN 100500 #define ll long long using namespace std; int mod; ll powM(ll a,int t=mod-2){ ll ret=1; while(t){ if (t&1)ret=ret*a%mod; a=a*a%mod;t>>=1; }return ret; } ll fac[MaxN],ifac[MaxN]; ll sC(int n,int m){ if (n<m)return 0; return fac[n]*ifac[m]%mod*ifac[n-m]%mod; } ll C(ll n,ll m){ if (!m)return 1; return sC(n%mod,m%mod)*C(n/mod,m/mod)%mod; } void Init() { fac[0]=1; for (int i=1;i<mod;i++) fac[i]=fac[i-1]*i%mod; ifac[mod-1]=powM(fac[mod-1]); for (int i=mod-1;i;i--) ifac[i-1]=ifac[i]*i%mod; } void solve() { ll n,m; scanf("%lld%lld%d",&n,&m,&mod); m+=n;Init(); printf("%lld\n",C(m,n)); } int main() { int T;scanf("%d",&T); while(T--)solve(); return 0; } ``` - **例题** [P4345 [SHOI2015]超能粒子炮·改](https://www.luogu.com.cn/problem/P4345) **题意** : 多次给出 $n,k$ ,求 $\sum\limits_{i=0}^k\dbinom{n}{i}\pmod {2333}$ $T\leq 10^5;n,k\leq 10^{18}$,时限$\texttt{1s}$. 设 $p=2333$ ,显然这是个素数。 根据卢卡斯定理有 $\dbinom{n}{m}=\dbinom{\lfloor n/p\rfloor}{\lfloor m/p\rfloor}\dbinom{n\bmod p}{m\bmod p}$ $ANS=\sum\limits_{i=0}^k\dbinom{n}{i}$ $=\sum\limits_{i=0}^k\dbinom{\lfloor n/p\rfloor}{\lfloor i/p\rfloor}\dbinom{n\bmod p}{i\bmod p}$ 前半部分是块状变化的,我们分别枚举$\lfloor i/p\rfloor$ 和 $i\bmod p$。注意最后多出来的一个散块。 $=\sum\limits_{i=0}^{\lfloor k/p\rfloor-1}\dbinom{\lfloor n/p\rfloor}{i}\sum\limits_{j=0}^{p-1}\dbinom{n\bmod p}{j}+\dbinom{\lfloor n/p\rfloor}{\lfloor k/p\rfloor}\sum\limits_{j=0}^{k\bmod p}\dbinom{n\bmod p}{j}$ 注意到$\sum\limits_{i=0}^{\lfloor k/p\rfloor-1}\dbinom{\lfloor n/p\rfloor}{i},\quad\sum\limits_{j=0}^{p-1}\dbinom{n\bmod p}{j},\quad\sum\limits_{j=0}^{k\bmod p}\dbinom{n\bmod p}{j}$均是子问题。 设 $f(n,k)=\sum\limits_{i=0}^k\dbinom{n}{i}$ 则有 $f(n,k)=f(\lfloor n/p\rfloor,\lfloor n/k\rfloor-1)f(n\bmod p,p-1)+\dbinom{\lfloor n/p\rfloor}{\lfloor k/p\rfloor}f(n\bmod p,k\bmod p)$ 先预处理 $p\times p$ 的 $f$,这用组合数递推不难做到 $O(p^2)$. 然后就是求解 $\dbinom{\lfloor n/p\rfloor}{\lfloor k/p\rfloor}$ ,使用`Lucas`定理即可做到 $O(\log_p n)$ 观察参数 $n$ 的变化 : 不断除以 $p$。 然后由于 $f(n,k)$ 在 $k>n$ 时等于 $f(n,n)$ ,当 $n\leq p$ 时就已经达到边界,只需要递归 $O(\log_p n)$ 次。 复杂度$O(T\log_p^2n+p^2)$. [提交记录](https://www.luogu.com.cn/record/33866714) - **周边** : Kummer定理 $\color{blue}\text{定理:}$ $\dbinom{n+m}{m}$ 中素因子 $p$ 的个数 $=n,m$ 在 $p$ 进制下做加法的**进位次数**。 注意到 $n!$ 中 $p$ 的次数是 $\sum\limits_{i=0}\lfloor n/p^i\rfloor$ ,证明比较显然。 根据 $\dbinom{n+m}{m}=\dfrac{(n+m)!}{n!m!}$ 则因子 $p$ 的个数是 $\sum\limits_{i=0}\lfloor (n+m)/p^i\rfloor-\lfloor n/p^i\rfloor-\lfloor m/p^i\rfloor$ 当某一位上 $\lfloor n/p^i\rfloor$ 相当于在 $p$ 进制表示下去掉后 $i$ 位。 那么只有 $n+m$ 在这里进位了才能逃脱被去掉的命运而恰好多$1$. - **例题** : [CF582D Number of Binominal Coefficients](https://www.luogu.com.cn/problem/CF582D) **题意** : 给出参数 $lim,p,a$ ,求有多少个组合数 $\dbinom{n}{k}$ 是 $p^a$ 的倍数,且 $0\leq k \leq n \leq lim$. $p,a\leq 10^9,lim\leq 10^{1000}$,答案对$10^9+7$取模 ,时限$\texttt{4s}$. 根据 Kummer 定理,能将问题转化成这样的形式: 求两个数 $x,y$ 使得 $x+y\leq lim$ ,而且会在 $p$ 进制下产生多于 $a$ 个进位。 这看起来就很数位`DP`的样子。 设$f[i][j][0/1][0/1]$表示考虑了前$i$位,还需要产生$j$个进位,是否卡上界,这一位是否进位。 注意这里是不需要记录前导 $0$ 的,而且由于进位会影响上一位,还要钦定是否进位。 转移的时候分几类讨论 : (下一位进/不进位)(这一位进/不进位),分别计算可能的数对个数即可。 复杂度$O(\log^2lim)$ ```cpp ``` - **中段综合测试** : [P2480 [SDOI2010]古代猪文](https://www.luogu.com.cn/problem/P2480) **题意** : 给出$n,g$,求$g^{\small\sum\limits_{k|n}\binom{n}{k}}$,对$999911659$取模。 $n,g\leq 10^9$,时限$\texttt{1s}$. 首先,模数是个质数,根据费马小定理,指数要对 $mod-1$ 取模。 则对 $999911658=2\times 3\times 4679\times 35617$ 取模。 观察到没有重复的素因子,这比较和善。 求出分别模 $2,3,4679,35617$ 的结果,然后使用中国剩余定理合并即可(甚至不需要`Exgcd`)。 求组合数可以使用Lucas定理。 复杂度是$O(d(n)\log p+T(2+3+4679+35617))$ - ## 扩展欧拉定理(降幂塔) 上集我们已经证明了 : $[a\perp m]\ \Longrightarrow\ a^{\varphi(m)}=1\pmod m$ 现在我们来看扩展形式, $\color{blue}\text{定理:}$ $$a^n=\begin{cases}a^{n\bmod\varphi(m)}&(a\perp m)\\a^n&(a\not\perp m,\ n< \varphi(m))\\a^{(n\bmod \varphi(m))+\varphi(m)}&(a\not\perp m,\ n\geq\varphi(m))\end{cases}\large\pmod m$$ 第一种情况已被证明,第二种情况是显然的,现在我们来证明第三种情况。 说人话就是 : 当 $a\not\perp m$ 时,第一个 $\varphi(m)$ 不是循环,从第二个才开始进入。 不妨先考虑 $a=p^k$ 的情况。 此时若有 $p\not\perp m$ ,则 $m$ 可以表示成 $s*p^c$ ,且 $s\perp p$。 那么根据普通欧拉定理有 $p^{\varphi(s)}=1\pmod s$ 。 同时,由于 $\varphi$ 函数的积性,有 $\varphi(s)|\varphi(m)$。 于是有 $p^{\varphi(m)}=1\pmod s$ 两边同时乘以 $p^c$ ,能够得到 $p^{\varphi(m)+c}=p^c\pmod {m}$ 注意到 $\varphi(m)\geq \varphi(p^c)\geq p^{c-1}(p-1)\geq c$ 所以有 $p^c=p^{c+\varphi(m)}=p^{c\bmod\varphi(m)+\varphi(m)}\pmod m$ ,我们证明了对于 $p^c$ 满足结论。 结论同时也对于 $p^{kc}$ 成立,可以归纳证明。 若有 $2c\geq n\geq \varphi(m) \geq c$ ,能推知 $n-c\leq \varphi(m)$ 则 $p^n=p^{c}p^{n-c}=p^{c\bmod\varphi(m)+\varphi(m)+(n-c)\bmod\varphi(m)}$ 有可能会出现 $c|n$ ,此时已经证毕。否则有 $c\bmod\varphi(m)+(n-c)\bmod\varphi(m)=n\bmod\varphi(m)$ 然后对 $n$ 的上界归纳即可。 现在我们对任意的 $p^n$ 证明了 $p^n=p^{(n\bmod\varphi(m))+\varphi(m)}$ 然后使用唯一分解定理即可把结论扩展至全体正整数。 - **例题** : [P4139 上帝与集合的正确用法](https://www.luogu.com.cn/problem/P4139) **题意** : 求 $2^{2^{2^{2^{\dots}}}}\bmod m$ , $\ p\leq 10^7$ 设$G(p)=2^{2^{2^{2^{\dots}}}}\bmod m$ 根据扩展欧拉定理能得到 $G(p)=2^{G(\varphi(p))+\varphi(p)}$ 边界就是 $2^{2^{2^{2^{\dots}}}}\bmod 1=0$ 取多少次 $\varphi(p)$ 会得到 $1$ 呢? 注意到欧拉函数的其中一个定义式 : $\varphi(m)=m\prod\limits_{p|m}\frac{p-1}{p}$ 当 $m$ 无素因子 $2$ 时,一个奇素因子一定会产生因子 $2$ ,因为 $p-1$ 是偶数。 当 $m$ 有素因子 $2$ 时,值至少减半。 综上,经过 $O(\log p)$ 次递归就能得到 $1$。 线性筛出欧拉函数复杂度是 $O(p+T\log^2p)$ 的。 [提交记录](https://www.luogu.com.cn/record/33847265) - **附加题** : - [P4861 按钮](https://www.luogu.com.cn/problem/P4861) 若 $\gcd(k,m)>1$ ,显然无解。 否则满足经典欧拉定理,则有 $k^{\varphi(m)}=1\pmod m$ ,但是 $\varphi(m)$ 并不是答案,可能存在更小的循环节。 可能的循环节一定是 $\varphi(m)$ 的约数,否则能导出矛盾。 于是只需要判定 $\varphi(m)$ 的约数即可,复杂度为 $O(\sqrt{m}\log m)$。 [提交记录] () - [P3934 Nephren Ruq Insania](https://www.luogu.com.cn/problem/P3934) 考虑乘方降幂塔,在 $O(\log p)$ 层之后模数就会变为 $1$,所以我们只需要暴力取出 $l$ 后的若干项即可。 上一题中,由于 $2^{2^{2^{2^{\dots}}}}$ 恒大于 $\varphi(m)$ ,所以总可以使用第三种情况。 现在由于可能产生第二种情况,我们计算时需要按照如下原则: 如果算的的数大于 $m$ 则替换成 $(a\bmod m)+m$,否则不变。 [提交记录](https://www.luogu.com.cn/record/33849679) - [CF906D Power Tower](https://www.luogu.com.cn/problem/CF906D) : 上一题的静态版,但数据较强。 - [P3747 [六省联考2017]相逢是问候](https://www.luogu.com.cn/problem/P3747) 仍然考虑结论 : 降幂塔不超过 $O(\log p)$ 层。某个位置被赋值 $O(\log p)$ 次之后就必然变为 $c^{c^{c^{\dots}}}\bmod p$。 考虑把不再变化的一整个区间冻结,可以证明只会产生 $O(n\log p)$ 次修改叶子的操作。 每次单点修改需要计算一次降幂塔,朴素实现复杂度是$O(\log p^2)$的,无法通过。 模数只有 $O(\log p)$ 个,可以分别于处理 $c$ 的光速幂。 总复杂度为$O(\sqrt{p}\log p+m\log ^2p)$。 [提交记录](https://www.luogu.com.cn/record/33862724) - [P5240 Derivation](https://www.luogu.com.cn/problem/P5240?contestId=14994) - ## [P4777 【模板】扩展中国剩余定理(EXCRT)](https://www.luogu.com.cn/problem/P4777) 仍然是若干个方程组$\begin{cases}x=c_1\pmod {p_1}\\x=c_2\pmod {p_2}\\...\\x=c_m\pmod {p_m}\end{cases}$ 求$x$的最小解,但不保证$p_{1...m}$互质。 仍然考虑两两合并$\begin{cases}x=c_1\pmod{p_1}\\x=c_2\pmod {p_2}\end{cases}$ 原先我们要直接构造某个模数在另一个模数下的逆,现在似乎不太行了。 第一个方程的通解为$c_1+kp_1$,现在就是求一个 $k$ 使得$c_1+kp_1=c_2\pmod{p_2}$ 移项有$kp_1=c_2-c_1\pmod{p_2}$,熟练的同学已经知道怎么做了。 等价于$kp_1+yp_2=c_2-c_1$ 根据斐蜀定理,方程有解的充要条件是$\gcd(p_1,p_2)|(c_2-c_1)$. 如果可解,先扩欧求出$kp_1+yp_2=\gcd(p_1,p_2)$,然后乘以$\dfrac{c_2-c_1}{\gcd(p_1,p_2)}$即可。 注意,新的模数是$lcm(p_1,p_2)$。此外还要注意防止溢出。 复杂度仍然是$O(m\log p)$. ```cpp #include<cstdio> #define ll long long using namespace std; inline ll mul(ll a,ll b,ll m){ ll d=((long double)a/m*b+0.5); ll r=a*b-d*m; return r<0?r+m:r; } ll gcd(ll a,ll b) {return !b ? a : gcd(b,a%b);} void exgcd(ll a,ll b,ll &x,ll &y){ if (b==0){x=1;y=0;return ;} exgcd(b,a%b,y,x);y-=(a/b)*x; } int n; ll c1,p1,c2,p2; int main() { scanf("%d",&n); c1=0;p1=1; for (int i=1;i<=n;i++){ scanf("%lld%lld",&p2,&c2); ll pd=gcd(p1,p2),sp=p2/pd*p1,k,y; c2=(c2-c1%p2+p2)%p2; //if (c2%pd) No Sol; exgcd(p1,p2,k,y); k=mul(k,(c2/pd),p2); c1=(c1+mul(p1,k,sp))%sp; p1=sp; }printf("%lld",c1); return 0; } ``` - **例题** : [P4774 [NOI2018]屠龙勇士](https://www.luogu.com.cn/problem/P4774) 发现不会做这道题是写本文的动力之一。 首先容易发现,屠龙的规则和顺序严格确定,那么如果我们能成功,每一轮使用的剑是相同的。拿`multiset`维护一下便可。 设杀第$i$条龙的剑攻击力为$k_i$. 击杀每条龙的条件很像取模,我们能列出式子$k_ix=a_i\pmod {p_i}$ 问题在于,可能有$a_i>p_i$,此时可能在比$0$大但为$p_i$倍数的血量停下来,所以$x$满足上述方程并不是充要条件。 考虑对每条龙记录砍到负数的最小次数的最大值,最后$x$在通解中取大一点就好了。 接下来的问题就是解方程组$\begin{cases}k_1x=c_1\pmod {p_1}\\k_2x=c_2\pmod {p_2}\\...\\k_mx=c_m\pmod {p_m}\end{cases}$ 考虑将$kx=c\pmod p$变为形如$x=c\pmod p$的经典形式。 先特判$0$ : 当$p|k$且$p|c$时方程恒成立,不考虑。当$p|k$但$p\!\!\not|\ c$时无解。 由于$k,p$不一定互质,我们不能直接通过逆元来转化。 不过,我们仍然能尝试求解$kx+py=c$,无解则原方程组无解。 否则由斐蜀定理得$\gcd(k,p)|c$,设$d=\gcd(k,p)$。 则有$\dfrac{k}{d}x+\dfrac{p}{d}y=\dfrac{c}{d}$. 我们对$\dfrac{p}{d}$取模即得到$\dfrac{k}{d}x=\dfrac{c}{d}\pmod {\dfrac{p}{d}}$ 由于$\dfrac{k}{d}\perp\dfrac{p}{d}$,我们就可以求逆元变为经典形式了。 剩下的就是一个扩展中国剩余定理。 ```cpp #include<algorithm> #include<cstdio> #include<set> #define Itor set<ll>::iterator #define ll long long #define MaxN 100500 using namespace std; const int mod=998244353; ll mul(ll a,ll b,ll m) {return (((a>>20)*b%m<<20)+(a-(a>>20<<20))*b)%m;} ll gcd(ll a,ll b) {return !b ? a : gcd(b,a%b);} ll lcm(ll a,ll b) {return a/gcd(a,b)*b;} void exgcd(ll a,ll b,ll &x,ll &y){ if (!b){x=1;y=0;return ;} exgcd(b,a%b,y,x);y-=(a/b)*x; } ll inv(ll a,ll m){ ll x,y;exgcd(a,m,x,y); return (x+m)%m; } bool fl; void merge(ll &c1,ll &p1,ll c2,ll p2) { ll c=(c2-c1%p2+p2)%p2,d=gcd(p1,p2); if (c%d){puts("-1");fl=1;return ;} ll k,y;exgcd(p1,p2,k,y); ll p=lcm(p1,p2); c1=(c1+mul(mul(k,p1,p),c/d,p))%p; p1=p; } multiset<ll> s; ll a[MaxN],k[MaxN],p[MaxN],t[MaxN] ,mx,tc,tp; void calc(ll k,ll c,ll p) { mx=max(mx,(c-1)/k+1); if (k%p==0){ if (c%p==0)return ; else {puts("-1");fl=1;return ;} } ll d=gcd(k,p); if (c%d){puts("-1");fl=1;return ;} k/=d;p/=d;c/=d; c=mul(c,inv(k,p),p); merge(tc,tp,c,p); } int n,m; void solve() { scanf("%d%d",&n,&m); for (int i=1;i<=n;i++)scanf("%lld",&a[i]); for (int i=1;i<=n;i++)scanf("%lld",&p[i]); for (int i=1;i<=n;i++)scanf("%lld",&t[i]); s.clear(); for (int i=1;i<=m;i++){ ll x;scanf("%lld",&x); s.insert(x); } for (int i=1;i<=n;i++){ Itor it=s.upper_bound(a[i]); if (it!=s.begin())it--; k[i]=*it;s.erase(it); s.insert(t[i]); } mx=0;tp=1;tc=0;fl=0; for (int i=1;i<=n&&!fl;i++) calc(k[i],a[i],p[i]); if (fl)return ; ll tk=mx/tp; while(tk*tp+tc<mx)tk++; printf("%lld\n",tk*tp+tc); } int main() { int T;scanf("%d",&T); while(T--)solve(); return 0; } ``` - ## [P4720 【模板】扩展卢卡斯](https://www.luogu.com.cn/problem/P4720) 跟普通卢卡斯定理关系不大 ( 我们把模数分解成$\prod p_i^{c_i}$的形式,对于每个$p^c$单独求解,然后使用普通中国剩余定理合并即可。 考虑$\dbinom{n}{m}=\dfrac{n!}{m!(n-m)!}\pmod{p^c}$ 但这毫无作用,往往有 $p|(n!)$ ,这样就无法求逆了。 考虑将$p$的幂次提尽,设$v(n)$为$n$中含有$p$的幂次。 则有$\dfrac{n!}{p^{v(n)}}≠0\pmod{p^c}$,这样就可以求逆了。设$r(n)=\dfrac{n!}{p^{v(n)}}$ 我们计算$\dfrac{r(n)}{r(m)r(n-m)}p^{v(n)-v(m)-v(n-m)}$即可得到$\dbinom{n}{m}$。 上文介绍了$v(n)=\sum\limits_{i=0}\lfloor n/p^i\rfloor$ ,现在问题变为求$r(n)$. 对于$n!=1*2*3*...*n$,我们可以把其中为$p$的倍数的项提出来。 变为$\Big[1*2*...*(p-1)\Big]*p*\Big[(p+1)(p+2)...(p+p-1)\Big]*2p*...$ 对于中间的每一块,$\prod_{i=1}^{p-1}(kp+i)\pmod {p^c}$,可以变为$[0,p^c)$中的某一段连乘积,维护阶乘及其逆元即可。 这样的段会有$O(n/p)$个,但是由于$\bmod\ p^c$,我们可以批量计算。 具体见代码,注意散块的边界情况。这样计算一次的复杂度是$O(p^c)$的。 对于那些为$p$的倍数的位置,共$\lfloor n/p\rfloor$个,统一除以$p$之后,又变成了$1*2*3*...$ 于是就变为求解$r(\lfloor n/p\rfloor)$. 预处理复杂度为$O(\sum p_i^{c_i})$,回答一次的复杂度为$O(\log^2 p)$. ( 如果采用欧拉定理+光速幂可以达到$O(\sqrt{p}+\sum p_i^{c_i})$预处理$O(\log p)$回答 ) ```cpp #include<algorithm> #include<cstdio> #define MaxN 1005000 #define ll long long using namespace std; void exgcd(ll a,ll b,ll &x,ll &y){ if (b==0){x=1;y=0;return ;} exgcd(b,a%b,y,x);y-=(a/b)*x; } ll inv(ll a,ll m){ ll x,y;exgcd(a,m,x,y); return (x%m+m)%m; } ll powM(ll a,ll t,int mod) { ll ret=1; while(t){ if (t&1)ret=ret*a%mod; a=a*a%mod;t>>=1; }return ret; } ll v(ll n,int p){ ll ret=0; while(n)ret+=(n/=p); return ret; } ll sav[MaxN]; ll r(ll n,int p,int mod){ if (n==0)return 1; return powM(sav[mod-1],n/mod,mod)*sav[n%mod]%mod*r(n/p,p,mod)%mod; } ll C(ll n,ll m,int p,int mod) { int c=v(n,p)-v(m,p)-v(n-m,p); ll ans=powM(p,c,mod); if (!ans)return 0; sav[0]=1; for (int i=1;i<mod;i++) if (i%p==0)sav[i]=sav[i-1]; else sav[i]=sav[i-1]*i%mod; ans=ans*r(n,p,mod)%mod; ans=ans*inv(r(m,p,mod),mod)%mod; ans=ans*inv(r(n-m,p,mod),mod)%mod; return ans; } ll ret,mp=1; void add(ll c,ll p) { ll sp=mp*p; ret=(ret*inv(p,mp)%sp*p+mp*inv(mp,p)%sp*c)%sp; mp=sp; } ll n,m;int p; int main() { scanf("%lld%lld%lld",&n,&m,&p); int d=2; while(d*d<=p){ if (p%d==0){ int mod=1; while(p%d==0){ p/=d; mod*=d; }add(C(n,m,d,mod),mod); }d++; }if (p>1)add(C(n,m,p,p),p); printf("%lld",ret); return 0; } ``` - **例题①** : [P2183 [国家集训队]礼物](https://www.luogu.com.cn/problem/P2183) **题意** : 有$n$个不同的小球,投入$m$个盒子里面,钦定第$i$个盒子要投$w_i$个球,问方案数。 对 $mod$ 取模,保证 $mod\leq 10^9$ 且分解之后的每个 $p^c\leq 10^5$ $m\leq 5,n\leq 10^9$ ,时限$\texttt{1s}$. 先判掉无解的情况。设$S[k]=\sum\limits_{i=1}^kw_i$ 不难发现题目叫我们求的是$\prod\limits_{i=1}^m\dbinom{n-S[i-1]}{w_i}$ 也就是在固定模数下求解$m$次组合数,套用`EXLucas`即可。 理论复杂度为$O(\sqrt{p}+\sum p^c+m\log^2p)$,但是$O\Big(m(\sqrt{p}+\sum p^c+\log^2p)\Big)$也能过我就偷懒了。 [评测记录](https://www.luogu.com.cn/record/32493149) - **例题②** : [P3726 [AH2017/HNOI2017]抛硬币](https://www.luogu.com.cn/problem/P3726) [题解Link](https://www.luogu.com.cn/blog/command-block/solution-p3726)