noip模拟赛 花

【问题描述】
商店里出售n种不同品种的花。为了装饰桌面,你打算买m支花回家。你觉得放两支一样的花很难看,因此每种品种的花最多买1支。求总共有几种不同的买花的方案?答案可能很大,输出答案mod p的值。

【输入格式】
一行3个整数n,m,p,意义如题所述。

【输出格式】
一个整数,表示买花的方案数。

【输入输出样例1】
4 2 5

1

【输入输出样例1说明】
用数字1,2,3,4来表示花的种类的话,4种花里买各不相同的2支的方案有(1,2)、(1,3)、(1,4)、(2,3)、(2,4)、(3,4),共6种方案,模5后余数是1。

【输入输出样例2】
见选手目录下的flower / flower2.in与flower / flower2.out

【数据范围】
对于30%的数据,n,m≤10
对于50%的数据,n,m≤1000
对于80%的数据,1≤m≤n≤50,000
对于100%的数据,1≤m≤n≤1,000,000,p≤1,000,000,000

分析:有点技巧的数学题。n,m很大,不能递推来求,p可能是个合数,不能求逆元,唯一的方法就是尽量把C(n,m)给缩小.因为C(n,m)可以表示为一个除法式子,将其变小的一般方法就是约分,对每个数分解质因数,在次数上加加减减就可以了,最后套上快速幂.

      直接上裸的会T,需要一些优化.筛法用上线性筛,在分解质因数的时候只分解没有出现过的,分解的过程中如果要分解的数是一个质数了就要退出.

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

typedef long long ll;

ll n, m, p, cnt, prime[1000010], ans = 1, tot[1000010];
bool vis[1000010];

void init()
{
    for (ll i = 2; i <= 1000000; i++)
    {
        if (!vis[i])
            prime[++cnt] = i;
        for (int j = 1; j <= cnt; j++)
        {
            if (i * prime[j] > 1000000)
                break;
            vis[i * prime[j]] = 1;
            if (i % prime[j] == 0)
                break;
        }
    }
}

void work(ll p,int x)
{
    ll t = p;
    if (!vis[p])
    {
        tot[p] += x;
        return;
    }
    for (int i = 1;prime[i] * prime[i] <= t && i <= cnt; i++)
    {
        if (p == 1)
            return;
        if (p % prime[i] == 0)
        {
            int tott = 0;
            while (p % prime[i] == 0)
            {
                p /= prime[i];
                tott++;
            }
            tot[prime[i]] += tott * x;
        }
        if (!vis[p])
        {
            tot[p] += x;
            return;
        }
    }
    if (x)
        tot[p] += x;
}

ll qpow(ll a, ll b, ll mod)
{
    ll res = 1;
    while (b)
    {
        if (b & 1)
            res = (res * a) % mod;
        a = (a * a) % mod;
        b >>= 1;
    }
    return res;
}

int main()
{
    init();
    scanf("%lld%lld%lld", &n, &m, &p);
    for (ll i = n; i >= m + 1; i--)
        work(i, 1);
    for (ll i = 1; i <= n - m; i++)
        work(i, -1);
    for (ll i = 1; i <= cnt; i++)
        if (tot[prime[i]])
            ans = (ans * qpow(prime[i], tot[prime[i]], p)) % p;
    printf("%lld
", ans);

    return 0;
}
原文地址:https://www.cnblogs.com/zbtrs/p/7729121.html