CodeChef:Fombinatorial(数论 & 逆元)

You are given a function f which is defined as :




Your task is to find the value of f(N) / ( f(r)*f(N-r) )

. As it will be a large number output it modulo M.

Input

First line contains T , number of test cases to follow.

Next follows T test case.

First line of ever test case contain 3 space separated integers N , M and Q.

Next Q line follows , each line contain r.

Output

For every test case , output Q lines , each line containing the answer.

Constraints

  • 2 ≤ N ≤ 105
  • 1 ≤ M ≤ 109
  • 2 ≤ Sum of N over all test cases ≤ 5*105
  • 2 ≤ Sum of Q over all test cases ≤ 105
  • 1 < r < N

Subtasks

  • Subtask 1: N ≤ 5 , rest of the constraints are same. Points - 9
  • Subtask 2: N ≤ 500 , M is a prime number , rest of the constraints are same. Points - 31
  • Subtask 3: Refer to constraints above : Points-60

Sample Input

1 5 24 2 2 3

Sample Output

8 8

Explanation

f[1] = 1

f[2] = 4

f[3] = 1*22*33 = 108

f[4] =1*22*33*44 = 27648

f[5] = 1*22*33*44*55 =86400000

value of f[5] / (f[2]*f[3]) = 200000 and 200000%24 is 8

题意:给N,M,Q,Q个询问,每个询问给个r,输出f(N) / ( f(r)*f(N-r) )对m取模。  

思路:假如f(x)均与m互素,那么可以预处理f(N)然后求出分子的逆元即可,由于m不一定为素数求逆元用扩展欧几里方便一点,那么目标就是将f(x)变成与m互素,即将f(1~N)分解质因子,出现f(N)的质因子均提出来另外存起来,最后分成两部分计算即可。

# include <bits/stdc++.h>
# define LL long long
# define M map<LL, LL>
# define A first
# define B second
using namespace std;
const int maxn = 1e5+30;
int t, n, q, cnt=2, p[maxn]={2,3};
LL gd, m, x, y, co[maxn];
M nco[maxn];
void init()
{
    for(int i=5, j=0; i<maxn-1; ++i,j=0)
    {
        for(;p[j]<=sqrt(i); ++j)
            if(i%p[j] == 0)
                break;
        if(i%p[j] !=0 ) p[cnt++] = i;
    }
}
LL qmod(LL a, LL b)
{
    LL ans = 1;
    for(;b;b>>=1)
    {
        if(b&1) ans = ans*a%m;
        a =a*a%m;
    }
    return ans;
}
LL ex_gcd(LL a, LL b)
{
    if(b == 0)
    {
        x = 1;
        y = 0;
        return a;
    }
    gd = ex_gcd(b, a%b);
    LL t = x;
    x = y;
    y = t-(a/b)*y;
    return gd;
}
int main()
{
    init();
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%lld%d",&n,&m,&q);
        for(int i=1; i<=n; ++i) co[i] = 1LL, nco[i].clear();
        for(int i=0; p[i]<=n; ++i)
        {
            for(int j=1; j*p[i]<=n; ++j)
            {
                LL tmp = j*p[i], tot=0, num = tmp;
                while(tmp%p[i] == 0)
                    ++tot, tmp /= p[i];
                tot = num*tot;
                if(__gcd(m, (LL)p[i]) != 1)
                    nco[num][p[i]] += tot;
                else
                    co[num] = co[num]*qmod(p[i], tot)%m;
            }
        }
        for(int i=2; i<=n; ++i)
        {
            co[i] = co[i] * co[i-1]%m;
            for(auto it = nco[i-1].begin(); it != nco[i-1].end(); ++it)
                nco[i][it->A] += it->B;
        }
        while(q--)
        {
            LL r;
            scanf("%lld",&r);
            LL ans = co[r]*co[n-r]%m;
            ex_gcd(ans, m);
            ans = (x%m+m)%m;
            for(auto it = nco[n].begin(); it != nco[n].end(); ++it)
            {
                LL tmp = qmod(it->A, it->B - nco[r][it->A] - nco[n-r][it->A]);
                ans = ans*tmp%m;
            }
            ans = ans*co[n]%m;
            printf("%lld\n",ans);
        }
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值