[Codeforces 893E. Counting Arrays]排列组合

[Codeforces 893E. Counting Arrays]排列组合

分类:combinatorics number theory math

1. 题目链接

[Codeforces 893E. Counting Arrays]

2. 题意描述

q 次询问,第i次询问包含两个数 x,y
求满足下面两个要求的 F 数组的方案数。
1. Fy
2. x=yi=1Fi
A B不同当且仅当至少存在一个数 i1iy 满足 AiBi 。答案对 109+7 取模
数据范围: 1q105,1xi,yi106

3. 解题思路

Step 1: 对 x 进行质因数分解。
Step 2: 在不考虑正负性时,然后单独考虑每一个质因子的对答案的贡献。如果质因子出现t次,那么问题就是转化为将 t 个球放到y个盒子中,球同,盒子不同的排列组合模型,贡献就是 Cty+t1 ,这个也可以通过插板法进行计算。
Step 3: 现在我们来考虑正负性对答案的贡献。很明显,前面 y1 个盒子我可以随意放 1 或者 1 。最后一个盒子的正负性根据前面y1个盒子来确定,贡献为 2y1
Step 4:对每个质因子、以及正负性的贡献累乘就得到答案了。


计算排列组合 Ckn 的姿势有很多。
这里由于 n106 ,我们可以用预处理阶乘、以及阶乘逆元的方法,用 O(1) 的算法求出。
如果模数比较小,可以用Lucas算法求出。
如果 k 非常非常小,(实际上这个题目k也不会很大, klog2106 ),直接一遍for循环也可以求出。

4. 实现代码

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef long double lb;
typedef unsigned int uint;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef pair<ull, ull> puu;
typedef pair<lb, lb> pbb;
typedef vector<int> vi;

const int inf = 0x3f3f3f3f;
const ll infl = 0x3f3f3f3f3f3f3f3fLL;
template<typename T> inline void umax(T &a, T b) { a = max(a, b); }
template<typename T> inline void umin(T &a, T b) { a = min(a, b); }
template<typename T> inline T randIntv(const T& a, const T& b) { return (T)rand() % (b - a + 1) + a; }
void debug() { cout << endl; }
template<typename T, typename ...R> void debug (T f, R ...r) { cout << "[" << f << "]"; debug (r...); }

const ll MOD = 1e9 + 7;
const int MX = 2e6 + 5;
int q, x, y;
ll prime[MX + 1];
pii fac[300];
ll p2[MX], pro[MX], inv[MX];

int getFac(ll x) {
    int sz = 0;
    for (int i = 1; prime[i] <= x / prime[i]; ++i) {
        fac[sz] = make_pair(prime[i], 0);
        if (x % prime[i] == 0) {
            while (x % prime[i] == 0) {
                ++ fac[sz].second;
                x /= prime[i];
            }
            ++ sz;
        }
    }
    if (x != 1) fac[sz ++] = make_pair(x, 1);
    return sz;
}

ll qpow(ll a, ll b) {
    ll ret = 1;
    while (b > 0) {
        if (b & 1) ret = ret * a % MOD;
        a = a * a % MOD;
        b >>= 1;
    }
    return ret;
}

ll C(int n, int k) {
    if (n < k) return 0;
    return pro[n] * inv[k] % MOD * inv[n - k] % MOD;
}

int main() {
#ifdef ___LOCAL_WONZY___
    freopen("input.txt", "r", stdin);
#endif // ___LOCAL_WONZY___
    p2[0] = 1;
    for (int i = 1; i < MX; ++i) p2[i] = p2[i - 1] * 2 % MOD;
    for (int i = 2; i <= MX; ++i) {
        if (!prime[i]) prime[++prime[0]] = i;
        for (int j = 1; j <= prime[0] && prime[j] <= MX / i; ++j) {
            prime[prime[j]*i] = 1;
            if (i % prime[j] == 0) break;
        }
    }
    pro[0] = 1;
    for (int i = 1; i < MX; ++i) {
        pro[i] = pro[i - 1] * i % MOD;
    }
    inv[MX - 1] = qpow(pro[MX - 1], MOD - 2);
    for (int i = MX - 2; i >= 0; --i) {
        inv[i] = inv[i + 1] * (i + 1) % MOD;
    }
    scanf("%d", &q);
    for (int i = 1; i <= q; ++i) {
        scanf("%d %d", &x, &y);
        int sz = getFac(x);
        ll ans = p2[y - 1];
        for (int i = 0; i < sz; ++i) {
            ans *= C(fac[i].second + y - 1, fac[i].second);
            ans %= MOD;
        }
        printf("%lld\n", ans);
    }
#ifdef ___LOCAL_WONZY___
    cout << "Time elapsed: " << 1.0 * clock() / CLOCKS_PER_SEC * 1000 << "ms." << endl;
#endif // ___LOCAL_WONZY___
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值