【JZOJ 3851】reward

24 篇文章 0 订阅
17 篇文章 0 订阅

Description

Bsny最近公司运作不佳,本年度利润才m元,但员工的奖金还是要发的,公司有n个员工,怎么发奖金这个完全由老板Bsny自己决定。Bsny想要么把这m元全发了,激励一下员工,但具体怎么分配方案有很多。比如m=1, n=2, 那么可以员工1发1元,员工2发0元;也可以员工1发0元,员工2发1元,有两种方案。
但其实,Bsny还是有点吝啬的,他想这m元不一定全部作为奖金,可以部分留给自己,这样的话,发奖金的方案数就更多了。还是以m=1, n=2为例子:
方案1:员工1发1元,员工2发0元
方案2:员工1发0元,员工2发1元
方案3:员工1发0元,员工2发0元
意味着老板Bsny发的奖金范围为[0, m]。
好奇的Bsny想知道,给定n和m,他有多少种发奖金的方案?这个答案很大,所以再给定一个 p ,最终的答案取模p的余数.
对于 p :设p=pc11pc22pc33pctt pi 为质数。
对于100%的数据: 1n,m1091pici105 ,所有p不超过 2311

Analysis

想到了什么,SDOI2013方程!
抽象: X1+X2+Xn=m,0Xim
形象:将 m 个球放入n个盒子,某个盒子可能为空。
为空?转化,把上式每个 Xi+1 ,等式右边就加 n
这样又变成不能为空的情况了,对于一个m答案就是 Cn1n+m1
总的答案就是 mk=0Cn1n+k1=Cnn+m ,至于为什么,可以自己画一个杨辉三角出来。
于是变成了标准的组合数取模。具体方法可以看上面的题目链接的题解。

Code

#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const int N=100010;
int num,tot;
ll n,m,p,fac[N];
struct lyd
{
    ll x,y,z;
}c[N];
void prime(ll n)
{
    num=0;
    for(int i=2;i*i<=n;i++)
        if(n%i==0)
        {
            c[++num].x=i,c[num].y=0;
            for(;n%i==0;n/=i,c[num].y++);
        }
    if(n>1) c[++num].x=n,c[num].y=1;
}
ll qmi(ll x,ll n,ll mo)
{
    ll t=1;
    for(;n;n>>=1)
    {
        if(n&1) t=t*x%mo;
        x=x*x%mo;
    }
    return t;
}
void exgcd(ll a,ll b,ll &x,ll &y)
{
    if(!b)
    {
        x=1,y=0;
        return;
    }
    ll x1,y1;
    exgcd(b,a%b,x1,y1);
    x=y1,y=x1-a/b*y1;
}
ll ny(ll n,ll mo)
{
    ll x,y;
    exgcd(n,mo,x,y);
    return (x%mo+mo)%mo;
}
ll calcfac(ll n,int k,int lyd)
{
    if(n<c[k].x) return fac[n];
    ll mo=c[k].z;
    tot+=lyd*(n/c[k].x);
    return fac[n%mo]*qmi(fac[mo],n/mo,mo)%mo*calcfac(n/c[k].x,k,lyd)%mo;
}
ll CC(ll m,ll n,int k)
{
    ll mo=c[k].z;
    fac[0]=1;
    fo(i,1,mo)
        if(i%c[k].x==0) fac[i]=fac[i-1];
        else fac[i]=fac[i-1]*i%mo;
    tot=0;
    ll t=calcfac(m,k,1)*ny(calcfac(n,k,-1)*calcfac(m-n,k,-1)%mo,mo)%mo;
    return t*qmi(c[k].x,tot,mo)%mo;
}
ll C(ll m,ll n)
{
    ll ans=0;
    fo(i,1,num)
    {
        ll Mi=p/c[i].z,mo=c[i].z;
        (ans+=Mi*ny(Mi,mo)%p*CC(m,n,i)%p)%=p;
    }
    return ans;
}
int main()
{
    scanf("%lld %lld %lld",&n,&m,&p);
    prime(p);
    fo(i,1,num) c[i].z=qmi(c[i].x,c[i].y,p+1);
    printf("%lld\n",C(n+m,n));
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值