zoukankan      html  css  js  c++  java
  • CF1559E Mocha and Stars

    CF1559E Mocha and Stars

    思路

    观察数据范围,大概 (Theta(nmlog 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(msqrt{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;
    }
    
  • 相关阅读:
    Linux 升级make (gmake)
    C库函数-calloc()
    redis若干命令 中文翻译
    centos7 安装xinetd,telnet
    vim 显示行号
    重启redis
    TS 过滤 .meta文件
    TS 判断为空
    TS 聚合查询 读取MongoDB
    windows 编译libuv库.txt
  • 原文地址:https://www.cnblogs.com/ullio/p/15154272.html
Copyright © 2011-2022 走看看