E-Mocha and Stars [ Codeforces Round #738 (Div. 2) ] DP 容斥

链接:https://codeforces.com/contest/1559/problem/E

题意:
给n个区间,要求从每个区间里取一个数,满足以下条件
1.取出来数的总和小于m
2.取出来所有数的gcd=0;

题解:

先考虑如果没有gcd的条件时怎么做。
设置dp[i][j]
i是当前取到第i个区间
j是取出的所有数总和
转移时显然有
dp[i][j]=dp[i-1][j-d]
(其中d为第i个区间选择的数)
复杂度nm2
用前缀和对其进行优化
dp[i][j]=sum(dp[i-1][j-r] ~ dp[i-1][j-l])
优化后复杂度为nm

不加gcd限制,算出来的是:当gcd是1的倍数时的情况数

考虑对所有区间与总和m同除k,再用上边的方法计算,算出来的是:当gcd是k的倍数的情况数。复杂度n*m * ln(m)

那么将k从m向1枚举,减去k的倍数的情况数,剩下的就是gcd恰好为k的情况数

输出是1的情况数即可

注意初始化用memset会T,必须初始化放循环里,用完就设置成0

#include<bits/stdc++.h>
using namespace std ;
typedef long long ll;
const int maxn=1e5+10;
const ll mod=998244353;
int l[maxn];
int r[maxn];
ll dp[60][maxn];
ll cnt[maxn];
ll sum[maxn];
int main(){
    ios::sync_with_stdio(0);
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        cin>>l[i]>>r[i];
    }
    for(int k=1;k<=m;k++){

        int top=m/k;
        
        dp[0][0]=1;
        for(int i=1;i<=n;i++){

            int tl=(l[i]+k-1)/k;
            int tr=r[i]/k;

            sum[0]=dp[i-1][0];
            for(int j=1;j<=top;j++){
                sum[j]=sum[j-1]+dp[i-1][j];
                sum[j]%=mod;
                dp[i-1][j]=0;
            }

            for(int j=tl;j<=top;j++){
                dp[i][j]+=sum[j-tl]-((j-tr-1<0)?0:sum[j-tr-1]);
                dp[i][j]%=mod;
            }

        }

        for(int i=1;i<=top;i++){
            cnt[k]+=dp[n][i];
            dp[n][i]=0;
            cnt[k]%=mod;
        }
    }

    for(int i=m;i>0;i--){
        for(int j=2;i*j<=m;j++){
            cnt[i]-=cnt[i*j];
            cnt[i]+=mod;
            cnt[i]%=mod;
        }
    }

    cout<<cnt[1]<<endl;
    //system("pause");
    return 0;
}
  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值