• P3807 【模板】卢卡斯定理/Lucas 定理


    原题链接

    简要题意:\(T\) 组数据,每次给定 \(n,m,p\) ,求 \(C_{n+m}^m \mod p\) 的值。

    \(T \leq 10 , 1 \leq n,m,p \leq 10^5\) 且保证 \(p\) 为素数。

    其实这道题目不需要用 lucas 定理来做,同样可以通过。lucas 可以做到 \(\mathcal{O}(\log_n p * p)\),那如果我们什么都不知道,能做到何种程度呢?

    \[C_{n+m}^m = \frac{(n+m)!}{n! \cdot m!} \]

    这是我们首先想到的。

    考虑 \(p\) 为素数,我们可以预处理阶乘部分,对于 \(n! , m!\) 我们可以求逆元。逆元可以线性预处理。

    但是注意到,\(n>p\) 或者 \(m>p\) 的时候,分母似乎没有逆元啊?

    请注意到这个分数是一个整数,所以我们 一定可以 将分子与分母所有 \(p\) 的倍数全部约掉。如果没有约掉,说明答案为 \(0\),这是显然的。

    好,那么怎么约?其实暴力扫一遍我们就能很好的解决问题(虽然我们应该并不需要)。

    暴力的做法就是,直接 \(1 \rightarrow n\) 扫过去,把 \(p\) 的幂次计出来,除剩下的数乘一起。

    这个时间复杂度是 \(\mathcal(O)(\log_n p * n + p)\).

    但实际上,统计 \(n!\)\(p\) 的幂次是可以做到更优复杂度的。这里笔者未实现,但是做法是有的。

    观察到其实 \(p\) 的幂次统计出来并不难,\(\log_n p\) 次就可以完成。

    对于其它数的乘积,本质上我们求的是

    \[1 \times 2 \times \cdots \times (p-1) \times (p+1) \times (p+2) \times \cdots \times (2p-1) \times \cdots \times n \]

    \(n!\) 中扣掉 \(p\) 的倍数,剩下的数是个什么情况。其实这是连续 \(\lfloor \frac{n}{p} \rfloor + 1\) 个块,其中除了最后一个块,其余块长均为 \(p-1\).

    于是我可以预处理 \(n\) 以内所有数阶乘的逆元,对于每个块 \(kp+1 - k(p+1)-1\),显然答案为 \((k(p+1)-1)! \cdot \text{inv}((kp)!)\).

    注意到这玩意儿已经可以 \(\mathcal{O}(n)\) 计算了,即枚举 \(k\) 然后暴力算。

    时间复杂度:$\mathcal{O}(\log_n p + n + p).

    注:lucas 适用于 \(n,m\) 很大但 \(p\) 很小的情况。暴力做法则对于 \(n,p\) 都有较大限制。

    #include <bits/stdc++.h>
    using namespace std;
    
    typedef long long ll;
    int T,n,m,p;
    
    const int N=2e5+1;
    int inv[N];
    ll ans,k;
    
    inline void calc1(int x) {
    	while(x % p == 0) x /= p , k++;
    	ans = ans * x % p;
    }
    
    inline void calc2(int x) {
    	while(x % p == 0) x /= p , k--;
    	ans = ans * inv[x%p] % p;
    }
    
    int main() {
    	scanf("%d",&T);
    	while(T--) {
    		scanf("%d %d %d",&n,&m,&p);
    		inv[0] = inv[1] = 1;
    		for(int i=2;i<N;i++) 
    			inv[i] = (1ll * (p-p/i) * inv[p%i]) % p;
    		ans = 1; k = 0;
    		for(int i=1;i<=n+m;i++) calc1(i);
    		for(int i=1;i<=n;i++) calc2(i);
    		for(int i=1;i<=m;i++) calc2(i);
    		if(k) puts("0");
    		else  printf("%lld\n",ans);
    	}
    	return 0;
    }
    
    简易的代码胜过复杂的说教。
  • 相关阅读:
    【收藏】UICrawler
    【转】 持续集成实战
    【工具引入】uiautomatorviewer 查找元素后自动生成代码
    【转】区块链测试简介
    【收藏】UI自动化测试基本规则与设计模式
    【转】自动化测试框架: pytest&allure ,提高自动化健壮性和稳定性
    【转】iOS 自动化性能采集
    ATX 安卓设备 WiFi 统一管理以及设备自动化测试
    【转】 mysql 数据优化
    【转】chrome devtools protocol——Web 性能自动化
  • 原文地址:https://www.cnblogs.com/bifanwen/p/15731905.html
Copyright © 2020-2023  润新知