容斥定理count GCD

2 篇文章 0 订阅
2 篇文章 0 订阅

传送门

题意:给定一个数组a_1, a_2, .....a_n;且任一项都在[1, m]区间内,要求构造一个数组b,使得对于任意i有gcd(b_1, b_2,.....b_i) = a[i]成立;且任意b_i也在[1, m]区间内。

思路:gcd(b_1, b_2,.....b_i) = a[i] 转化为gcd(a[i-1], b[i]) = a[i]. 可以看出a[i-1]一定要整除a[i],否则无解,在得到即问题转换为在1到中找到与互质的数字个数。

#include <bits/stdc++.h>
#pragma GCC optimize (2)
#define oo 0x3f3f3f3f
#define ll long long
#define OO 0x3f3f3f3f3f3f3f3f
#define IO ios::sync_with_stdio(false);cin.tie(nullptr)
#define rep(i,a,n) for (ll i=a;i<=n;i++)
#define per(i,a,n) for (ll i=a;i>=n;i--)
#define int ll
const int N = 2e5 + 10, M = 2 * N;
using namespace std;
typedef pair<int, int> pii;
#define deb(i,x) if(int i==x) int k = 1; 
#define all(x) x.begin(),x.end()
ll lowbit(ll x) { return x & -x; }
ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a; }
ll qmi(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;
}
const int mod = 998244353;
signed main()
{
    IO;
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
#endif
    int t; cin >> t;
    while (t--)
    {
        int n, m; cin >> n >> m;
        vector<int>a(n);
        for (int i = 0; i < n; i++)cin >> a[i];
        bool f = true;
        int ans = 1;
        for (int i = 1; i < n; i++)
        {
            if (a[i - 1] % a[i] != 0)
            {
                f = false;
                break;
            }
            else
            {
                int d = a[i - 1] / a[i];
                vector<int> p;
                int temp = d;
                for (int i = 2; i <= temp / i; i++)
                {
                    if (temp % i == 0)
                    {
                        p.push_back(i);
                        while (temp % i == 0)temp /= i;
                    }
                }
                if (temp > 1)p.push_back(temp);
                int sz = p.size();
                auto cal = [&](int x)->int
                {
                    int num = 0;
                    for (int i = 0; i < 1ll << sz; i++)
                    {
                        int k = 1;
                        int cnt = 0;
                        for (int j = 0; j < sz; j++)
                            if (i >> j & 1) k *= p[j], cnt++;
                        if (cnt & 1)
                            num -= x / k;
                        else
                            num += x / k;
                    }
                    return num;
                };
                ans = (ans * cal(m / a[i])) % mod;
            }
        }
        if (f)
            cout << ans << endl;
        else
            cout << 0 << endl;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值