Codeforces Round #738 (Div. 2) E

首先可以想到 题目如果通过正面来求的话 那么所需要求的种类很复杂 所以很容易想到容斥的思想 我们就可以想到 将所有组建情况再减去 gcd不是 1 的情况即可
1.组建所有情况 就可以考虑 dp 前 i个数 组成 m 的种类有多少 很明显 是个背包 但是 复杂度为 nm * (ri - li)所以超时 考虑优化 因为 ri ~ li是连续的 所以可以使用前缀和优化至 2nm
2.gcd不是1的情况 那么就可以变成 gcd 为2 为 3.。。。。为 m的情况 考虑2的时候 我们可以把所有2的倍数往上放 3的时候 所有3的倍数往上放 但是又有个问题 2会产生 6的 3也会产生 6的 所以又要减去多余的 那么就继续利用 dp 考虑 第 i 个需要减去 或者 加上多少 由于 第 i个数 会对所以 i的倍数产生 影响所以 只需要 nsqrtn 去往前找因子的影响即可
3.如何实现 减去 和1.是同一思想 我们可以想到 i的倍数 组成的数 一定是 i的倍数 所以无论如何 产生的数 的个数 一定是调和级数个的 那就是 nlogn的复杂 再 50 就是 nlogn50 大约是 7e7 加上常数和mod 和剪汁 刚好能跑过去

#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
#include<cmath>
#include<queue>
#include<map>

using namespace std;

const int N = 55,M = 1e5 + 10,mod = 998244353;
typedef pair<int,int> PII;
typedef long long ll;

int n,m;

int flag[M],cnt,prime[M],is[M];
int l[N],r[N];
void is_prime(){
    
    
    for(int i = 2; i < M; i++){
    
    
        if(!flag[i]){
    
    
            flag[i] = 1;
            prime[++cnt] = i;
        }
        for(int j = 1; i * prime[j] < M; j++){
    
    
            flag[i * prime[j]] = 1;
            if(i % prime[j] == 0){
    
    
                break;
            }
        }
    }
}

ll dp[N][M],Pref[M];
ll dp2[N][M],pref[M];

ll mos(ll a){
    
    
    a %= mod;
    a += mod;
    a %= mod;
    return a;
}

int main(){
    
    
    is_prime();
    cin >> n >> m;

    for(int i = 1; i <= n; i++){
    
    
        scanf("%d%d",&l[i],&r[i]);
    }

    for(int i = 0; i <= m; i++){
    
    
        Pref[i] = 1;
    }

    ll sum = 0;
    for(int i = 1; i <= n; i++){
    
    
        for(int j = l[i]; j <= m; j++){
    
    
            if(j > r[i]){
    
    
                dp[i][j] += Pref[j - l[i]] - Pref[j - r[i] - 1];
            }else dp[i][j] += Pref[j - l[i]];
            dp[i][j] %= mod;
        }
        Pref[0] = dp[i][0];
        for(int j = 1; j <= m; j++){
    
    
            Pref[j] = Pref[j - 1] + dp[i][j];
            Pref[j] %= mod;
        }
    }

    sum = Pref[m];
    
     for(int j = 2; j <= m; j++){
    
    
        int dd = 0;
        for(int i = 2; i * i <= j; i++){
    
    
            if(j % i == 0 && j != i * i){
    
    
                dd += is[i];
                dd += is[j / i];
            }
            if(j == i * i) dd += is[i];
        }
        is[j] = -1 - dd;
    }



    for(int j = 2; j <= m; j++){
    
    
        if(is[j] == 0) continue;
        for(int i = 0; i <= m / j; i++) pref[i] = 1;
        for(int i = 1; i <= n; i++){
    
    
            int d = (l[i] + j - 1) / j * j;
            if(d > r[i]){
    
    
                pref[m / j] = 0;
                break;
            }
            for(int k = d; k <= m; k += j){
    
    
                if(r[i] / j >= k / j){
    
    
                    dp2[i][k] += pref[k / j - d / j];
                }else dp2[i][k] += mos(pref[k / j - d / j] - pref[k / j - r[i] / j - 1]);
                dp2[i][k] %= mod;
            }
            pref[0] = dp2[i][0];
            for(int k = 1; k <= m / j; k++){
    
    
                pref[k] = pref[k - 1] + dp2[i][k * j];
                pref[k] %= mod;
            }
        }

        //47 50
        sum += is[j] * pref[m / j];

        sum %= mod;

        for(int i = 1; i <= n; i++){
    
    
            for(int k = (l[i] + j - 1) / j * j; k <= m; k += j){
    
    
                dp2[i][k] = 0;
            }
        }
    }


    cout << mos(sum) << endl;




	return 0;
}

猜你喜欢

转载自blog.csdn.net/qqqingyi/article/details/119756291
今日推荐