CF1559E Mocha and Stars(dp+莫比乌斯反演)

求有多少长 n n n 的序列 a a a 满足:

  • l i ≤ a i ≤ r i l_i\le a_i\le r_i liairi
  • ∑ i = 1 n a i ≤ m \sum_{i=1}^{n}a_i\le m i=1naim
  • gcd ⁡ ( a 1 , … , a n ) = 1 \gcd(a_1,\dots,a_n)=1 gcd(a1,,an)=1

答案对 998244353 998244353 998244353 取模。

样例输入 #1

2 4
1 3
1 2

样例输出 #1

4

只看前两个条件,我们发现就直接是一个背包问题,枚举 a i a_i ai,枚举背包状态,然后遍历所有的位置,时间复杂度是 O ( m 2 n ) O(m^2n) O(m2n),很显然,这个时间复杂度不能满足我们的要求,需要注意的是,题目问的是 < = m <=m <=m

dp[0]=1;//意义是:恰好等于下标的种类数
for(int i=1;i<=n;i++){
    memset(f,0,sizeof f);
    for(int j=l[i];j<=r[i];j++){
        for(int k=j;k<=m;j++){
            f[k]+=dp[k-j];
        }
    }
    dp[0]=0;
    for(int j=1;j<=m;j++)dp[i]=dp[i]+f[i];
}

但是其实我们发现,中间的状态转移,用前缀和就可以完美解决

for(int i=0;i<=m;i++)dp[i]=1;//意义是:小于等于下标的种类数(因此使用前缀和)
for(int i=1;i<=n;i++){
    memset(f,0,sizeof f);
    for(int j=l[i];j<=m;j++){
        f[j]+=dp[j-l[i]];
        if(j>r[i]){
            f[j]-=dp[j-r[i]-1];//要[l[i],r[i]]区间的数,大于r[i]的数无法转移,因此减掉
        }
    }
    dp[0]=0;
    for(int j=1;j<=m;j++)dp[i]=dp[i-1]+f[i];
}

时间复杂度是 O ( n m ) O(nm) O(nm)还可以~

于是现在求的问题就是

∑ a 1 = l [ 1 ] r [ 1 ] ∑ a 2 = l [ 2 ] r [ 2 ] . . . ∑ a n = l [ n ] r [ n ] f ( a 1 , a 2 . . . a n ) ∗ [ g c d ( a 1 , a 2 . . . a n ) = 1 ] \displaystyle\sum_{a_1=l[1]}^{r[1]}\displaystyle\sum_{a_2=l[2]}^{r[2]}...\displaystyle\sum_{a_n=l[n]}^{r[n]}f(a_1,a_2...a_n)*[gcd(a_1,a_2...a_n)=1] a1=l[1]r[1]a2=l[2]r[2]...an=l[n]r[n]f(a1,a2...an)[gcd(a1,a2...an)=1]

转换一下

∑ a 1 = l [ 1 ] r [ 1 ] ∑ a 2 = l [ 2 ] r [ 2 ] . . . ∑ a n = l [ n ] r [ n ] f ( a 1 , a 2 . . . a n ) ∗ ε ( g c d ( a 1 , a 2 . . . a n ) = 1 ) \displaystyle\sum_{a_1=l[1]}^{r[1]}\displaystyle\sum_{a_2=l[2]}^{r[2]}...\displaystyle\sum_{a_n=l[n]}^{r[n]}f(a_1,a_2...a_n)*ε(gcd(a_1,a_2...a_n)=1) a1=l[1]r[1]a2=l[2]r[2]...an=l[n]r[n]f(a1,a2...an)ε(gcd(a1,a2...an)=1)

展开

∑ a 1 = l [ 1 ] r [ 1 ] ∑ a 2 = l [ 2 ] r [ 2 ] . . . ∑ a n = l [ n ] r [ n ] f ( a 1 , a 2 . . . a n ) ∗ ∑ d ∣ g c d ( a 1 , a 2 . . . a n ) μ ( d ) \displaystyle\sum_{a_1=l[1]}^{r[1]}\displaystyle\sum_{a_2=l[2]}^{r[2]}...\displaystyle\sum_{a_n=l[n]}^{r[n]}f(a_1,a_2...a_n)*\displaystyle\sum_{d|gcd(a_1,a_2...a_n)}^{}μ(d) a1=l[1]r[1]a2=l[2]r[2]...an=l[n]r[n]f(a1,a2...an)dgcd(a1,a2...an)μ(d)

交换枚举顺序

∑ d = 1 m μ ( d ) ∑ a 1 = l [ 1 ] / d r [ 1 ] / d ∑ a 2 = l [ 2 ] / d r [ 2 ] / d . . . ∑ a n = l [ n ] / d r [ n ] / d f ( a 1 ∗ d , a 2 ∗ d . . . a n ∗ d ) \displaystyle\sum_{d=1}^{m}μ(d)\displaystyle\sum_{a_1=l[1]/d}^{r[1]/d}\displaystyle\sum_{a_2=l[2]/d}^{r[2]/d}...\displaystyle\sum_{a_n=l[n]/d}^{r[n]/d}f(a_1*d,a_2*d...a_n*d) d=1mμ(d)a1=l[1]/dr[1]/da2=l[2]/dr[2]/d...an=l[n]/dr[n]/df(a1d,a2d...and)

枚举倍数是调和级数,时间复杂度是 O ( l n m ) O(ln m) O(lnm),枚举 n n n个数,那么时间复杂度是 n l n m nlnm nlnm,前面的 d d d枚举起来是 m m m,因此时间复杂度是 O ( n m l n m ) O(nmln m) O(nmlnm)

#include <bits/stdc++.h>
using namespace std;
const int N = 500005;
const int mod = 998244353;
int check[N + 5], n, m;
int mu[N + 5], p[N + 5];
bool flg[N + 5];
int dp[N];
int l[N];
int r[N];
void mobius()
{
    int tot = 0;
    mu[1] = 1;
    for (int i = 2; i <= N; ++i)
    {
        if (!flg[i])
        {
            p[++tot] = i;
            mu[i] = -1;
        }
        for (int j = 1; j <= tot && i * p[j] <= N; ++j)
        {
            flg[i * p[j]] = 1;
            if (i % p[j] == 0)
            {
                mu[i * p[j]] = 0;
                break;
            }
            mu[i * p[j]] = -mu[i];
        }
    }
}
signed main()
{
    mobius();
    cin >> n >> m;
    for (int i = 1; i <= n; i++)
        cin >> l[i] >> r[i];
    int ans = 0;

    for (int d = 1; d <= m; d++)
    {
        if (!mu[d])
            continue;
        int tot = 0;
        int sum = m / d;
        vector<int> dp(sum + 1, 1);
        for (int i = 1; i <= n; i++)
        {
            vector<int> f(sum + 1);
            for (int j = (l[i] + d - 1) / d; j <= sum; j++)
            {
                f[j] = dp[j - (l[i] + d - 1) / d];
                if (j >= r[i] / d + 1)
                    f[j] = (f[j] - dp[j - r[i] / d - 1] + mod) % mod;
            }
            dp[0] = 0;
            for (int j = 1; j <= sum; j++)
                dp[j] = (dp[j - 1] + f[j]) % mod;
        }
        ans = ((ans + dp[sum] * mu[d]) % mod + mod) % mod;
    }
    cout << ans << endl;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值