zoukankan      html  css  js  c++  java
  • [Gym101138G][容斥原理]LCM-er

    [Gym101138G][容斥原理]LCM-er

    题意描述

    给定(n,a,b,x)四个数字,需要计数满足如下条件序列的个数(答案对(10^9+7)取模),条件如下。

    [{a le A_1 le A_2 le A_3 le A_4 le cdots le A_n le b }\ 1 le nle 100,1le a,b,x le 10^9, ale b \ ]

    以及需要满足 (lcm(A_1,A_2,A_3,cdots ,A_n))可以被(x)整除, 即

    [{lcm(A_1,A_2,A_3,cdots ,A_n) | x} ]

    解法解析

    根据正难则反的原则,我们可以考虑计数那些(lcm)不可以整除的序列。

    仔细推断后发现,若将数(x)质因数分解为(p_1^{k_1}p_2^{k_2}cdots p_m^{k_m})

    那么当且仅当将某序列的(lcm)质因数分解后,存在某个质数的幂小于(x)质因数分解后(若没有对应质数就将幂视为(0))对应质数的幂,此序列不合法。

    [egin{aligned} x &= p_1^{k_1}p_2^{k_2}cdots p_m^{k_m} \ lcm &= p_1^{t_1}p_2^{t_2}cdots p_m^{t_m} \ end{aligned} \ 需要满足 k_i le t_i (1le ile m) ]

    注意到没有出现在(x)中的质数幂对答案无影响,所以我们只考虑(x)分解出来的质数幂。

    现在我们枚举(lcm​)中不合法的位置,由于至少有一个位置不合法不好计算,我们利用容斥原理转而计算出单个位置不合法,两个位置不合法...m个位置不合法的方案数,即:

    (q_i代表第i个位置合法的情况, S代表所有位置的集合{1,2,3,cdots,m},F代表满足指定情况下的方案数​)

    [egin{aligned} Fleft(igcap_{i=1}^m q_i ight) &= {All - Fleft(igcup_{i=1}^moverline{q_i} ight)} \ Fleft(igcup_{i=1}^moverline{q_i} ight) &= {F(overline{q_1})+F(overline{q_2})+cdots+F(overline{q_m})} \ &{-sum_{1le ilt jle m}F(overline{q_i}capoverline{q_j})} \ &{+sum_{1le ilt jlt kle m}F(overline{q_i}capoverline{q_j}capoverline{q_j})} \ &cdots \ &{+sum_{tsubseteq S}(-1)^{|t|}Fleft( igcap_{iin t}overline{q_i} ight)} end{aligned} ]

    现在我们需要关注的就是对于一个位置集合,如何计算 (Fleft( igcap_{iin t}overline{q_i} ight)​)

    容易发现若(lcm)在这些位置不满足,则构成序列的每一个数都不是选定位置质数幂的倍数,也就是所我们首先要筛选出([a,b]​)范围内有多少数字不是选定位置处质数幂的倍数即可。

    联想在小于(n)的数中筛选不是(2,3,5)倍数的数字,我们再次利用容斥即可。

    [G(q_i)代表[a,b]区间内不为对应i位置质数幂倍数的数字数\ {Gleft( igcap_{iin t}overline{q_i} ight) =(b-a+1)+sum_{jsubseteq t}(-1)^{|j|}left( Biglfloor frac{b}{prod_{xin j}p_x^{k_x}}Big floor - Biglfloor frac{a-1}{prod_{xin j}p_x^{k_x}}Big floor ight)} ]

    将筛出的数字看作一个集合,大小为(k),现在可以从中任意选取(n)个数,每个数选取次数不限,求最后构成序列的有序方案数;即计算该多重集的(n)组合,可构成的方案数为(inom{n+k-1}{n})(挡板法)。

    [{Fleft( igcap_{iin t}overline{q_i} ight) = inom{Gleft( igcap_{iin t}overline{q_i} ight)+n-1}{n}} ]

    整个过程需要两个容斥,做一次枚举子集即可,考虑到构成(x​)的质数幂不超过(9)个,复杂度(O(n2^{omega(n)}+3^{omega(n)}))

    PS:代码写得异常奇怪,就想试试不开数组只用vector

    #include <bits/stdc++.h>
    using namespace std;
    
    const int MOD = 1e9 + 7;
    const int BIT = 11;
    const int N = 105;
    vector<int> inv(N, 1);
    
    void init_inverse() {
        for (int i = 2; i < inv.size(); i++) {
            inv[i] = 1LL * (MOD - MOD / i) * inv[MOD % i] % MOD;
        }
    }
    
    void resolve(int x, vector<int>& factors) {
        for (int i = 2; i * i <= x; i++) {
            if (x % i == 0) {
                factors.push_back(i);
                x /= i;
                while (x % i == 0) {
                    *factors.rbegin() *= i;
                    x /= i;
                }
            }
        }
        if (x > 1) factors.push_back(x);
    }
    
    int calc(int x, int l, int r) {
        return r / x - (l - 1) / x;
    }
    
    int comb(int n, int m) {
        if (m > n || m < 0) return 0;
        int ret = 1;
        for (int i = 1; i <= m; i++) {
            ret = 1LL * ret * inv[i] % MOD;
            ret = 1LL * ret * (n - i + 1) % MOD;
        }
        return ret;
    }
    
    int main() {
        init_inverse();
    
        int n, a, b, x;
        vector<int> factors;
    
        cin >> n >> a >> b >> x;
        resolve(x, factors);
    
        vector<int> lcm(1 << factors.size(), 1);
        vector<int> weight(1 << factors.size(), 0);
        vector<int> cnt(1 << factors.size(), 0);
    
        for (int i = 0, j = 1; j < weight.size(); i++, j = j + j) {
            weight[j] = i;
        }
        for (int i = 1; i < lcm.size(); i++) {
            lcm[i] = lcm[i - (i & -i)] * factors[weight[i & -i]];
            cnt[i] = cnt[i - (i & -i)] + 1;
        }
    
        int ans = 0;
        for (int i = 0; i < lcm.size(); i++) {
            int temp = b - a + 1;
            for (int j = i; j; j = i & (j - 1)) {
                if (cnt[j] & 1) {
                    temp = (temp - calc(lcm[j], a, b) + MOD) % MOD;
                } else {
                    temp = (temp + calc(lcm[j], a, b)) % MOD;
                }
            }
            if (cnt[i] & 1) {
                ans = (ans - comb(temp + n - 1, n) + MOD) % MOD;
            } else {
                ans = (ans + comb(temp + n - 1, n)) % MOD;
            }
        }
        cout << ans << endl;
        
        return 0;
    }
    
  • 相关阅读:
    数据库设计步骤 java程序员
    (1) 语法结构 java程序员
    (3)JavaScript学习笔记 函数、对象、数组 java程序员
    JavaScript 学习计划 java程序员
    (5)JavaScript学习笔记 变量 java程序员
    (2) 数据类型、值 及 字符串 java程序员
    (4)JavaScript学习笔记 数据类型和值(续) java程序员
    J2EE、J2SE、J2ME是什么意思? java程序员
    80端口(该端口是Tomcat的监听端口)已经被其他程序占用 java程序员
    (17) string 和 stringbuilder java程序员
  • 原文地址:https://www.cnblogs.com/UnderSilenceee/p/10657707.html
Copyright © 2011-2022 走看看