2021-10-11

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

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值