P1680题解
P1680题解
思路
一看这题,不就是组合题吗?再往下看,涉及到分组,一下子就想到了隔板法,只不过要想隔板还需要一点小处理:因为第 i 必须大于 Ci,于是想到将 n 减去每组的 Ci,于是奇怪的分组就转化为基础的组合题:n 个相同的球放入 m 个不同盒子里,不能为空,求方案数。
那脑子里就出现了答案:
Cn−1m−1mod1000000007。
而由我们小学学的组合知识可知:
Cnm=n!m!(n−m)!
于是设 p=n!,q=m!(n−m)!,则 Cnm=pq。
我的方法就是先求出 1∼1000005 内所有数的阶乘 mod1000000007,然后就可以不咋费时间地求出 p,q。
由于除法取模不能直接除并取模,所以答案应为 a×b′mod100000007,此处的 b′ 为 b 在模 1000000007 意义下的逆元。因为 1000000007 是一个质数,所以求逆元时可以用费马小定理或者欧拉定理(我用的是费马小定理)。
总结
- 组合知识。
- 逆元。
- 费马小定理。
- 欧拉定理。
代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#define mod 1000000007
#define ll long long
using namespace std;
ll n,m,num[1000005]={1};
ll qpow(ll a, ll b){//快速幂
ll ans = 1;
for(; b; b>>=1,a=a*a%mod)
if(b&1) ans=ans*a%mod;
return ans;
}
int zh(ll n, ll m){//求C(n,m)
ll cnt=(num[n-m]*num[m])%mod;
return num[n]*qpow(cnt,mod-2)%mod;
}
int main(){
for(int i=1; i<1000001; i++)//先算出阶乘
num[i]=(num[i-1]*i)%mod;//不取模见祖宗
scanf("%lld %lld" ,&n,&m);
for(int i=1; i<=m; i++){
ll x;
scanf("%lld",&x);
n-=x;
}
printf("%d\n",zh(n-1,m-1));
return 0;
}
附(选看)
- 快速幂:
对于线性求解的问题,如果需要优化,很多时候可以考虑选用树形结构的数据结构或者分治思想,那样可以将算法时间复杂度从 O(n) 降低到 O(logn)。这里我们考虑用分治思想。
求 abmodn,其实很容易想到,若 b 是偶数,则 ab=ab2×ab2;若 b 是奇数,则 ab=a⌊b2⌋×a⌊b2⌋×a。即我们只要求出 ab2,然后再通过一次乘法运算,即可求出 ab。接着继续将 b2 分成 b4,b8……最终 b 会变成 1,并且只需要 logb 次就可以实现。这就是快速幂。
- 费马小定理:
若 p 是质数,则对于任意整数 a,都有 ap≡a(modp)。
- 欧拉定理:
若正整数 a,n 互质,则 aφ(n)≡1(modn),其中 φ(n) 为欧拉函数。
欧拉定理的推论:
若正整数 a,n 互质,则对于任意正整数 b,有 ab≡abmodφ(n)(modn)。
证明:
设 b=q×φ(n)+r,其中 0≤r<φ(n),即 r=bmodφ(n)。
于是:ab≡aq×φ(n)+r≡(aφ(n))q×ar≡1q×ar≡ar≡abmodφ(n)(modn)。
完结——如有错误请指出在哪里,是啥问题,谢谢!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现