CF1559E Mocha and Stars

CF1559E Mocha and Stars

思路

观察数据范围,大概 \(\Theta(nm\log m)\) 是可行的

那么有了一个基本的想法, \(dp_{i,j}\) 表示选到了第 i 位,且当前所选数的和是 j 的方法数

但是这样最后的 dp 值里面会有 gcd 不为 1 的情况

以下默认省略和不大于m

考虑容斥原理

如果 gcd 不为 1 ,那么它一定是某一个质数的倍数

所以减去所有 gcd 为质数倍数的情况,但是这样子我们会将一些 gcd 为特定值的部分多减去一次, 比如 gcd 为 6 的情况会被多减一次, 因为 2 减 3 也减

所以需要加上一些情况,可以发现所有因式分解下有两个及以上不同的质数的 gcd 都会被多减,那么加上 gcd 为所有因式分解中仅为两个不同素数的数(6, 15)的倍数的情况

会发现,这样又会有所有因式分解中有三个及以上不同的质数的 gcd 被多加,所以减去 gcd 为所有因式分解中仅为三个不同素数的数(30, 42)的倍数的情况

...递归下去

实际上,这种容斥系数有个专门的函数来表示,叫做莫比乌斯函数(我也是才知道)

暴力分解数的话,复杂度为 \(\Theta(m\sqrt{m})\)​ (wf算了1e12,但实际加一点剪枝远跑不满,本地循环内计数跑了1e8次)

但因为它是积性函数,也可以线性筛内递推求出来

那么知道容斥方法了,考虑怎么计算

如果要计算 gcd 为 x 的倍数的方案数,那么我们首先将 m/x , l/x(上取整), r/x(下取整)

这样当取一个数 i 的时候,相当于取了 i*x ,这就保证了答案是 gcd 为 x 的倍数的方案数

dp 时我做了一个前缀和优化,也可以另开一个数组用背包

代码

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

typedef long long ll;
typedef unsigned long long ull;
typedef const int& cint;

const int mod = 998244353;
const int inf_int = 0x7fffffff;
const ll inf_ll = 0x7fffffffffffffff;
const double ept = 1e-9;

int n, m;
ll l[51], r[51];
ll dp[51][100100];

ll sol(cint x) {
    for(int i=0; i<=m/x; i++) dp[0][i] = 1;
    for(int i=1; i<=n; i++) {
        for(int j=1; j<=m/x; j++) {
            dp[i][j] = 0;
            int dl = ceil((double)l[i]/x), dr = r[i]/x;
            if(dl > dr) return 0;
            if(j-dl >= 0) dp[i][j] += dp[i-1][j-dl];
            if(j-dr-1 >= 0) dp[i][j] -= dp[i-1][j-dr-1];
            dp[i][j] += dp[i][j-1];
            dp[i][j] = (dp[i][j] + mod) % mod;
        }
    }
    return dp[n][m/x];
}

int check(int x) {
    if(x == 1) return 1;
    int num = 0;
    int r = sqrt(x);
    for(int i=2; i<=r; i++) {
        if(!(x%(i*i))) return 2;
        if(!(x%i)) { 
            ++ num;
            x /= i;
        }
    }
    if(x > 1) ++num;
    return !(num&1);
}

int main() {
    cin >> n >> m;
    for(int i=1; i<=n; i++) cin >> l[i] >> r[i];
    ll ans = 0;
    for(int i=1; i<=m; i++) {
        if(check(i) == 1) ans += sol(i);
        else if(check(i) == 0) ans -= sol(i);
        ans = (ans+mod) % mod;
    }
    cout << ans << endl;
    return 0;
}
posted @ 2021-08-17 21:20  ullio  阅读(40)  评论(0编辑  收藏  举报