codeforces 数论&容斥原理 Count GCD

题目描述:

 题意:

给定一个长度为 n(n≤2∗105) 的数组 a ,问有多少种长度为 n 的数组 b 满足

  • 1 ≤ b[i] ≤ m(m≤109)
  • 对于任意的 i ,满足 gcd(b[1],b[2],b[3]...b[i])=a[i]

首先 gcd 操作是递减的, gcd(b[1],b[2],b[3]...b[i])≥gcd(b[1],b[2],b[3]...b[i],b[i+1])

也就是需要满足 a[i−1]≥a[i]

且有 b[1]=a[1]

gcd(b[1],b[2],b[3]...b[i−1])=a[i−1]

gcd(b[1],b[2],b[3]...b[i−1],b[i])=a[i]

着我们有 gcd(a[i−1],b[i])=a[i] 也就是 a[i−1]=k∗a[i] (就是 a[i] 是 a[i−1] 的一个因子

所以 a[i−1]%a[i]=0

这样 a 的合法条件就讨论完了。( a[i−1]%a[i]≠0 就包含了 a[i−1]<a[i] 这个信息)

考虑每个 b[i] 的可能。

由 gcd(a[i−1],b[i])=a[i] 得, b[i] 是 a[i] 的倍数。

a[i−1]=k1∗a[i],b[i]=k2∗a[i]   且  gcd(k1,k2)=1

也就是 k2∈[1,m/a[i]]   且   k2 与 a[i−1] / a[i] 互质。

那么问题就变成了,区间互质问题。我们需要容斥来解决

严格鸽:算法数学笔记(1) 容斥原理

问题是, n=2∗105 ,用容斥的复杂度是 m 啊。

但是我们注意到, a[i] 是 a[i−1] 的因子,也就是说如果 a[i]≠a[i−1] 的情况下。

a[i]≤a[i−1]2 ,是logn级别的递减,所以我们记忆下 (m/a[i],a[i−1]/a[i]) 即可

模板:

vector<int>prime;
int num = 0;
int sum = 0;
void pr(int n) {
    prime.clear();
    for (int i = 2; i * i <= n; i++) {
        if (n % i == 0) {
            prime.push_back(i);
            while (n % i == 0)n /= i;
        }
    }
    if (n > 1)prime.push_back(n);
}
void dfs(int pos, int mul, int len) {
    if (pos == prime.size()) {
        if (len) {
            if (len % 2)sum += num / mul;
            else sum -= num / mul;
        }
        return;
    }
    dfs(pos + 1, mul, len);
    dfs(pos + 1, mul * prime[pos], len + 1);
}
map<pair<int,int>, int>mp;//记忆化
int que(int L, int k) {
    if (mp.count({ L,k }))return mp[{L, k}];
    pr(k);
    num = L; sum = 0;
    dfs(0, 1, 0);
    return mp[{L, k}] = (L - sum);
}

const int N = 2e5 + 5;
int n, a[N], m;
const int mod = 998244353;
void slove() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++)cin >> a[i];
    for (int i = 2; i <= n; i++) {
        if (a[i - 1] % a[i]) {
            cout << 0 << endl;
            return;
        }
    }
    ll ans = 1;
    for (int i = 2; i <= n; i++) {
        ans *= que(m / a[i], a[i - 1] / a[i]);
        ans %= mod;
    }
    cout << ans << endl;
}

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值