B - RGB Coloring(组合数)

本文介绍了一种计算组合数C(m,n)模p的方法,利用费马小定理和快速幂技巧来解决除法在取模运算中不适用的问题,并提供了C++代码实例。

Time limit : 2sec / Memory limit : 1024MB

Score : 700 points

Problem Statement

Takahashi has a tower which is divided into N layers.Initially, all the layers are uncolored. Takahashi is going to paint some of the layers in red, green or blue to make a beautiful tower.He defines the beauty of the tower as follows:

  • The beauty of the tower is the sum of the scores of the N layers, where the score of a layer is A if the layer is painted red, A+B if the layer is painted green, B if the layer is painted blue, and 0 if the layer is uncolored.

Here, A and B are positive integer constants given beforehand. Also note that a layer may not be painted in two or more colors.

Takahashi is planning to paint the tower so that the beauty of the tower becomes exactly K.How many such ways are there to paint the tower? Find the count modulo 998244353.Two ways to paint the tower are considered different when there exists a layer that is painted in different colors, or a layer that is painted in some color in one of the ways and not in the other.

Constraints

  • 1≤N≤3×105
  • 1≤A,B≤3×105
  • 0≤K≤18×1010
  • All values in the input are integers.

Input

Input is given from Standard Input in the following format:

N A B K

Output

Print the number of the ways to paint tiles, modulo 998244353.


Sample Input 1

Copy

4 1 2 5

Sample Output 1

Copy

40

In this case, a red layer worth 1 points, a green layer worth 3 points and the blue layer worth 2 points. The beauty of the tower is 5 when we have one of the following sets of painted layers:

  • 1 green, 1 blue
  • 1 red, 2 blues
  • 2 reds, 1 green
  • 3 reds, 1 blue

The total number of the ways to produce them is 40.


Sample Input 2

Copy

2 5 6 0

Sample Output 2

Copy

1

The beauty of the tower is 0 only when all the layers are uncolored. Thus, the answer is 1.


Sample Input 3

Copy

90081 33447 90629 6391049189

Sample Output 3

Copy

577742975

是时候要补一下组合数的算法了,参考博客:http://www.cnblogs.com/liziran/p/6804803.html

现在目标是求Cmn%p

,p为素数(经典p=1e9+7)

虽然有Cmn=n!m!(nm)!

,但由于取模的性质对于除法不适用,所以Cmn%p≠(n!%pm!%p∗(nm)!%p)%p

所以需要把“除法”转换成“乘法”,才能借助取模的性质在不爆long long的情况下计算组合数。这时候就需要用到“逆元”!

  逆元:对于a和p(a和p互素),若a*b%p≡1,则称b为a%p的逆元。

那这个逆元有什么用呢?试想一下求(ab)

%p,如果你知道b%p的逆元是c,那么就可以转变成(ab)

%p = a*c%p = (a%p)(c%p)%p

那怎么求逆元呢?这时候就要引入强大的费马小定理!

  费马小定理:对于a和素数p,满足$a^{p-1}$%p≡1

接着因为ap−1

ap−2∗a,所以有ap−2∗a%p≡1!对比逆元的定义可得,ap−2

是a的逆元!

所以问题就转换成求解ap−2

,即变成求快速幂的问题了(当然这需要满足p为素数)。

现在总结一下求解Cmn%p

的步骤:

  1. 通过循环,预先算好所有小于max_number的阶乘(%p)的结果,存到fac[max_number]里 (fac[i] = i!%p)
  2. 求m!%p的逆元(即求fac[m]的逆元):根据费马小定理,x%p的逆元为xp−2

,因此通过快速幂,求解fac[m]p−2

%p,记为M求(n-m)!%p的逆元:同理为求解fac[nm]p−2%p,记为NMCmn%p

  1. = ((fac[n]*M)%p*NM)%p
  2. 用C++实现的代码,输入为n,m,p,要求0<=m<=n<=1e6(否则fac存不下),且gcd(p,n!)=1(即互素),输出为Cmn%p

 

//快速幂求x^n%mod 
long long pow_mod(long long x, long long n, long long mod) {
    long long res = 1;
    while (n) {
        if (n & 1) res = res * x % mod;
        x = x * x % mod;
        n >>= 1;
    }
    return res;
}
long long fac[MAX_NUMBER+5];
long long n, m, p;
int main() {
    while (~scanf("%lld %lld %lld", &n, &m, &p)) {        
        //预处理求fac,fac[i] = i!%p 
        fac[0] = 1;
        for (int i = 1; i <= n; i++) {fac[i] = fac[i - 1] * i % p;}
        //组合数 = n!*(m!%p的逆元)*((n-m)!%p的逆元)%p 
        printf("%lld\n", fac[n] * pow_mod(fac[m], p - 2, p) % p * pow_mod(fac[n - m], p - 2, p) % p);
    }
}
//快速幂求x^n%mod 
long long pow_mod(long long x, long long n, long long mod) {
    long long res = 1;
    while (n) {
        if (n & 1) res = res * x % mod;
        x = x * x % mod;
        n >>= 1;
    }
    return res;
}
long long fac[MAX_NUMBER+5];
long long n, m, p;
int main() {
    while (~scanf("%lld %lld %lld", &n, &m, &p)) {        
        //预处理求fac,fac[i] = i!%p 
        fac[0] = 1;
        for (int i = 1; i <= n; i++) {fac[i] = fac[i - 1] * i % p;}
        //组合数 = n!*(m!%p的逆元)*((n-m)!%p的逆元)%p 
        printf("%lld\n", fac[n] * pow_mod(fac[m], p - 2, p) % p * pow_mod(fac[n - m], p - 2, p) % p);
    }
}

 

方法二:半预处理 https://blog.youkuaiyun.com/u010582475/article/details/47707739
由于Lucas定理保证了阶乘的数均小于p,所以可以讲所有的阶乘先预处理,优化C(n,m)
mod的要求:p<10^6,且为素数
有效范围:1<=n,m<=10^9

 

//半预处理  
const ll MAXN = 100000;  
ll fac[MAXN+100];  
void init(int mod)  
{  
    fac[0] = 1;  
    for (int i=1; i<mod; i++){  
        fac[i] = fac[i-1] * i % mod;  
    }  
}  

//半预处理逆元求C(n,m)%mod  
ll C(ll n, ll m)  
{  
    if ( m>n ) return 0;  
    return fac[n] * (GetInverse(fac[m]*fac[n-m], mod)) % mod;  
}  

 

#include <iostream>

#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <cmath>
#include <algorithm>
#define ll long long
#define M 998244353
const int x=300005;
using namespace std;
ll fac[x]={1,1},inv[x]={1,1},f[x]={1,1};
ll C(ll a,ll b){
    if(b>a)return 0;
    return fac[a]*inv[b]%M*inv[a-b]%M;
}
void init(){
    for(int i=2;i<x;i++){
        fac[i]=fac[i-1]*i%M;
        f[i]=(M-M/i)*f[M%i]%M;
        inv[i]=inv[i-1]*f[i]%M;
    }
}
int main()
{
    ll N,A,B,K;
    cin>>N>>A>>B>>K;
    init();
    ll i;long long j,sum,ans=0;
    for(i=0;i<=N;i++){
        sum=i*A;
            if((K-sum)>=0&&(K-sum)%B==0){
                j=(K-sum)/B;
                if(j>N) continue;
                ans=ans+C(N,i)*C(N,j);
                ans%=M;
            }
    }
    cout<<ans<<endl;
    return 0;
}//代码中的组合数算法是别人提交的代码中的

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值