p1608 奇怪的分组
想不到用什么算法,无奈
学了别人的做法,隔板法,将n减去所有的Ci,将剩余的分配C(n-1,m-1)。
我们可以想象这 n 个人站成一列,拿 m−1 个板子往这 n−1 个间隔中插,其中任意两个都不在同一个间隔中。
#include <iostream>
#define M 1000000007
#define N 1000000
using namespace std;
typedef long long ll;
int n,m,ans;
ll f[N+10];
ll ppow(ll a,ll b)//快速幂
{
ll ans=1;
while(b)
{
if(b&1) ans=ans*a%M;
a=a*a%M;
b>>=1;
}
return ans;
}
int C(int n,int k)//组合数
{
ll x=(f[n-k]*f[k])%M;
int ans=f[n]*ppow(x,M-2)%M;
return ans;
}
int main()
{
f[0]=1;
cin>>n>>m;
for(int i=1;i<=N;i++) f[i]=f[i-1]*i%M;
for(int i=1;i<=m;i++)
{
int a;
cin>>a;
n-=a;
}
cout<<C(n-1,m-1)<<endl;
return 0;
}
一开始快速幂函数没有写对,b>>=1;忘记了最后的赋值
求组合数,都在这个过程中要用到逆元,因为会遇到除法取模易出现问题,需要用逆元知识将除法改成乘法,但数据太大,所以还要转化成幂的形式,这里要用到费马小定理。
推导推导,得a*b(p-2)mod p
重点在这里:理解乘法逆元这里有详细的例子(还不是很理解)
从题解中学的快读read(),确实快很多,还有人使用Lucas定理再加上快读直接0ms。。
#include <bits/stdc++.h>
#define M 1000000007
#define N 1000000
using namespace std;
typedef long long ll;
int n,m,ans;
ll f[N+10];
int read() {
char c;
int ans(0);
while(!isdigit(c = getchar()));
do ans = ans * 10 + c - 48;
while(isdigit(c = getchar()));
return ans;
}
ll ppow(ll a,ll b)//快速幂
{
ll ans=1;
while(b)
{
if(b&1) ans=ans*a%M;
a=a*a%M;
b>>=1;
}
return ans;
}
int C(int n,int k)//组合数
{
ll x=(f[n-k]*f[k])%M;
int ans=f[n]*ppow(x,M-2)%M;
return ans;
}
int main()
{
f[0]=1;
n=read();
m=read();
for(int i=1;i<=N;i++) f[i]=f[i-1]*i%M;//预处理阶乘
for(int i=1;i<=m;i++)
{
n-=read();
}
cout<<C(n-1,m-1)<<endl;
return 0;
}