P1680题解

P1680题解

思路

一看这题,不就是组合题吗?再往下看,涉及到分组,一下子就想到了隔板法,只不过要想隔板还需要一点小处理:因为第 i 必须大于 Ci,于是想到将 n 减去每组的 Ci,于是奇怪的分组就转化为基础的组合题:n 个相同的球放入 m 个不同盒子里,不能为空,求方案数。

那脑子里就出现了答案:

Cn1m1mod1000000007

而由我们小学学的组合知识可知:

Cnm=n!m!(nm)!

于是设 p=n!q=m!(nm)!,则 Cnm=pq

我的方法就是先求出 11000005 内所有数的阶乘 mod1000000007,然后就可以不咋费时间地求出 pq

由于除法取模不能直接除并取模,所以答案应为 a×bmod100000007,此处的 bb 在模 1000000007 意义下的逆元。因为 1000000007 是一个质数,所以求逆元时可以用费马小定理或者欧拉定理(我用的是费马小定理)。

总结

  1. 组合知识。
  2. 逆元。
  3. 费马小定理。
  4. 欧拉定理。

代码

#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;
}

附(选看)

  1. 快速幂:

对于线性求解的问题,如果需要优化,很多时候可以考虑选用树形结构的数据结构或者分治思想,那样可以将算法时间复杂度从 O(n) 降低到 O(logn)。这里我们考虑用分治思想。

abmodn,其实很容易想到,若 b 是偶数,则 ab=ab2×ab2;若 b 是奇数,则 ab=ab2×ab2×a。即我们只要求出 ab2,然后再通过一次乘法运算,即可求出 ab。接着继续将 b2 分成 b4b8……最终 b 会变成 1,并且只需要 logb 次就可以实现。这就是快速幂。

  1. 费马小定理:

p 是质数,则对于任意整数 a,都有 apa(modp)

  1. 欧拉定理:

若正整数 an 互质,则 aφ(n)1(modn),其中 φ(n) 为欧拉函数。

欧拉定理的推论:

若正整数 an 互质,则对于任意正整数 b,有 ababmodφ(n)(modn)

证明:

b=q×φ(n)+r,其中 0r<φ(n),即 r=bmodφ(n)

于是:abaq×φ(n)+r(aφ(n))q×ar1q×ararabmodφ(n)(modn)

完结——如有错误请指出在哪里,是啥问题,谢谢!

posted @   naroto2022  阅读(5)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示
花开如火,也如寂寞。