📜  O(1)时间复杂度中的nCr%p查询

📅  最后修改于: 2021-04-27 09:38:04             🧑  作者: Mango

给定Q个查询和P个,其中P是质数,每个查询都有两个数字N和R,任务是计算nCr mod p。
限制条件:

N <= 106
R <= 106
p is a prime number

例子:

天真的方法是通过在任何时候应用模块化运算来使用公式来计算nCr 。因此,时间复杂度将为O(q * n)。

更好的方法是使用费马小定理。根据它,nCr也可以写成(n!/(r!*(nr)!))mod,它等效于(n!* inverse(r!)* inverse((nr)!))mod p 。因此,从1到n的数字的预计算阶乘将允许在O(log n)中回答查询。唯一需要做的计算是计算r的逆!和(nr)!因此,总体复杂度将为q *(log(n))

一种有效的方法是通过预先计算阶乘的逆来将更好的方法减少为一种有效的方法。在O(n)时间内预计算阶乘逆,然后在O(1)时间内可以查询查询。使用模块化乘法逆可以在O(n)时间内计算1到N个自然数的逆。使用阶乘的递归定义,可以编写以下内容:

n! = n * (n-1) !
taking inverse on both side 
inverse( n! ) = inverse( n ) * inverse( (n-1)! )

由于N的最大值是10 6 ,所以可以进行直到10 6的预计算值。

下面是上述方法的实现:

C++
// C++ program to answer queries
// of nCr in O(1) time.
#include 
#define ll long long
const int N = 1000001;
using namespace std;
 
// array to store inverse of 1 to N
ll factorialNumInverse[N + 1];
 
// array to precompute inverse of 1! to N!
ll naturalNumInverse[N + 1];
 
// array to store factorial of first N numbers
ll fact[N + 1];
 
// Function to precompute inverse of numbers
void InverseofNumber(ll p)
{
    naturalNumInverse[0] = naturalNumInverse[1] = 1;
    for (int i = 2; i <= N; i++)
        naturalNumInverse[i] = naturalNumInverse[p % i] * (p - p / i) % p;
}
// Function to precompute inverse of factorials
void InverseofFactorial(ll p)
{
    factorialNumInverse[0] = factorialNumInverse[1] = 1;
 
    // precompute inverse of natural numbers
    for (int i = 2; i <= N; i++)
        factorialNumInverse[i] = (naturalNumInverse[i] * factorialNumInverse[i - 1]) % p;
}
 
// Function to calculate factorial of 1 to N
void factorial(ll p)
{
    fact[0] = 1;
 
    // precompute factorials
    for (int i = 1; i <= N; i++) {
        fact[i] = (fact[i - 1] * i) % p;
    }
}
 
// Function to return nCr % p in O(1) time
ll Binomial(ll N, ll R, ll p)
{
    // n C r = n!*inverse(r!)*inverse((n-r)!)
    ll ans = ((fact[N] * factorialNumInverse[R])
              % p * factorialNumInverse[N - R])
             % p;
    return ans;
}
 
// Driver Code
int main()
{
    // Calling functions to precompute the
    // required arrays which will be required
    // to answer every query in O(1)
    ll p = 1000000007;
    InverseofNumber(p);
    InverseofFactorial(p);
    factorial(p);
 
    // 1st query
    ll N = 15;
    ll R = 4;
    cout << Binomial(N, R, p) << endl;
 
    // 2nd query
    N = 20;
    R = 3;
    cout << Binomial(N, R, p) << endl;
 
    return 0;
}


Java
// Java program to answer queries
// of nCr in O(1) time
import java.io.*;
 
class GFG{
     
static int N = 1000001; 
 
// Array to store inverse of 1 to N
static long[] factorialNumInverse = new long[N + 1];
 
// Array to precompute inverse of 1! to N!
static long[] naturalNumInverse = new long[N + 1];
 
// Array to store factorial of first N numbers
static long[] fact = new long[N + 1];
 
// Function to precompute inverse of numbers
public static void InverseofNumber(int p)
{
    naturalNumInverse[0] = naturalNumInverse[1] = 1;
     
    for(int i = 2; i <= N; i++)
        naturalNumInverse[i] = naturalNumInverse[p % i] *
                                 (long)(p - p / i) % p;
}
 
// Function to precompute inverse of factorials
public static void InverseofFactorial(int p)
{
    factorialNumInverse[0] = factorialNumInverse[1] = 1;
 
    // Precompute inverse of natural numbers
    for(int i = 2; i <= N; i++)
        factorialNumInverse[i] = (naturalNumInverse[i] *
                           factorialNumInverse[i - 1]) % p;
}
 
// Function to calculate factorial of 1 to N
public static void factorial(int p)
{
    fact[0] = 1;
 
    // Precompute factorials
    for(int i = 1; i <= N; i++)
    {
        fact[i] = (fact[i - 1] * (long)i) % p;
    }
}
 
// Function to return nCr % p in O(1) time
public static long Binomial(int N, int R, int p)
{
     
    // n C r = n!*inverse(r!)*inverse((n-r)!)
    long ans = ((fact[N] * factorialNumInverse[R]) %
                       p * factorialNumInverse[N - R]) % p;
     
    return ans;
}
 
// Driver code
public static void main (String[] args)
{
     
    // Calling functions to precompute the
    // required arrays which will be required
    // to answer every query in O(1)
    int p = 1000000007;
    InverseofNumber(p);
    InverseofFactorial(p);
    factorial(p);
     
    // 1st query
    int n = 15;
    int R = 4;
    System.out.println(Binomial(n, R, p));
     
    // 2nd query
    n = 20;
    R = 3;
    System.out.println(Binomial(n, R, p));
}
}
 
// This code is contributed by RohitOberoi


Python3
# Python3 program to answer queries
# of nCr in O(1) time.
N = 1000001
 
# array to store inverse of 1 to N
factorialNumInverse = [None] * (N + 1)
 
# array to precompute inverse of 1! to N!
naturalNumInverse = [None] * (N + 1)
 
# array to store factorial of
# first N numbers
fact = [None] * (N + 1)
 
# Function to precompute inverse of numbers
def InverseofNumber(p):
    naturalNumInverse[0] = naturalNumInverse[1] = 1
    for i in range(2, N + 1, 1):
        naturalNumInverse[i] = (naturalNumInverse[p % i] *
                                   (p - int(p / i)) % p)
 
# Function to precompute inverse
# of factorials
def InverseofFactorial(p):
    factorialNumInverse[0] = factorialNumInverse[1] = 1
 
    # precompute inverse of natural numbers
    for i in range(2, N + 1, 1):
        factorialNumInverse[i] = (naturalNumInverse[i] *
                                  factorialNumInverse[i - 1]) % p
 
# Function to calculate factorial of 1 to N
def factorial(p):
    fact[0] = 1
 
    # precompute factorials
    for i in range(1, N + 1):
        fact[i] = (fact[i - 1] * i) % p
 
# Function to return nCr % p in O(1) time
def Binomial(N, R, p):
     
    # n C r = n!*inverse(r!)*inverse((n-r)!)
    ans = ((fact[N] * factorialNumInverse[R])% p *
                      factorialNumInverse[N - R])% p
    return ans
 
# Driver Code
if __name__ == '__main__':
     
    # Calling functions to precompute the
    # required arrays which will be required
    # to answer every query in O(1)
    p = 1000000007
    InverseofNumber(p)
    InverseofFactorial(p)
    factorial(p)
 
    # 1st query
    N = 15
    R = 4
    print(Binomial(N, R, p))
 
    # 2nd query
    N = 20
    R = 3
    print(Binomial(N, R, p))
 
# This code is contributed by
# Surendra_Gangwar


C#
// C# program to answer queries 
// of nCr in O(1) time
using System;
 
class GFG{
     
static int N = 1000001;  
 
// Array to store inverse of 1 to N 
static long[] factorialNumInverse = new long[N + 1]; 
   
// Array to precompute inverse of 1! to N! 
static long[] naturalNumInverse = new long[N + 1];
   
// Array to store factorial of first N numbers 
static long[] fact = new long[N + 1]; 
   
// Function to precompute inverse of numbers 
static void InverseofNumber(int p) 
{ 
    naturalNumInverse[0] = naturalNumInverse[1] = 1; 
       
    for(int i = 2; i <= N; i++) 
        naturalNumInverse[i] = naturalNumInverse[p % i] *
                                 (long)(p - p / i) % p; 
} 
   
// Function to precompute inverse of factorials 
static void InverseofFactorial(int p) 
{ 
    factorialNumInverse[0] = factorialNumInverse[1] = 1; 
   
    // Precompute inverse of natural numbers 
    for(int i = 2; i <= N; i++) 
        factorialNumInverse[i] = (naturalNumInverse[i] * 
                            factorialNumInverse[i - 1]) % p; 
} 
   
// Function to calculate factorial of 1 to N 
static void factorial(int p) 
{ 
    fact[0] = 1; 
   
    // Precompute factorials 
    for(int i = 1; i <= N; i++)
    { 
        fact[i] = (fact[i - 1] * (long)i) % p; 
    } 
} 
   
// Function to return nCr % p in O(1) time 
static long Binomial(int N, int R, int p) 
{ 
       
    // n C r = n!*inverse(r!)*inverse((n-r)!) 
    long ans = ((fact[N] * factorialNumInverse[R]) % 
                       p * factorialNumInverse[N - R]) % p; 
       
    return ans; 
}
 
// Driver code
static void Main()
{
     
    // Calling functions to precompute the 
    // required arrays which will be required 
    // to answer every query in O(1) 
    int p = 1000000007; 
    InverseofNumber(p); 
    InverseofFactorial(p); 
    factorial(p); 
       
    // 1st query 
    int n = 15; 
    int R = 4; 
    Console.WriteLine(Binomial(n, R, p));
       
    // 2nd query 
    n = 20; 
    R = 3; 
    Console.WriteLine(Binomial(n, R, p));
}
}
 
// This code is contributed by divyeshrabadiya07


Javascript


输出:
1365
1140

时间复杂度:预先计算为O(10 6 ),每个查询为O(1)。