E. Mocha and Stars 反演+dp

卷积:
两个多项式的卷积:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
之后引入 在这里插入图片描述
狄利克雷卷积是两个数的因子的成绩为n,且两个因子都要是整数。
突破卷积的目标系数为两个下标的和为p
可以简单的记忆为

而狄利克雷卷积有如此复杂的格式,自然有良好的性质。
以下列举本题所需的:
莫比乌斯函数与1的卷积为单位函数
形式化:
在这里插入图片描述
(其实以上狄利克雷卷积性质,即位莫比乌斯反演的最常见形式;)

单位函数是:
在这里插入图片描述
在这里插入图片描述

只有当自变量取1的时候,函数值为1
其余为0
即题目中的自变量为gcd(a_1,a_2,…,a_n)
根据以上内容可以把题目的目标写为:(g为和不超过m函数)
在这里插入图片描述
求此函数的值
在这里插入图片描述

根据莫比乌斯反演,将gcd(a_1,a_2,…,a_n)作为单位函数的自变量:
在这里插入图片描述
d是gcd的因子,而gcd是所有的共同因子,那么可以表述为d是每个a_i的因子
在这里插入图片描述
注意,以下将会发生一个神奇的变化:
在这里插入图片描述
(这里的a_i与上一个公式的a_i不同,详细看如下内容)
本人认为以上变换才是莫比乌斯反演题目的最难部分,相当于一个整除分块,把d提取出来,然后对于a_i 来说在无d的限制条件的情况下其取值范围为l_i,r_i
而加上限制条件之后变成了,l_i,r_i内的d的倍数的数
简述为最小值为[(l_i + d - 1) / d]*d 即向上取整
最大为[r_i / d]*d 这里的 ‘/’ 是C++运算符。

至此,题目即可枚举d然后进行DP,由于调和级数所以每次dp为nlogm
加上枚举d总复杂为m
nlogm
这里的dp有些技巧
先假设dp[i][j]为选前i个a,和为j,如果当前取值k
那么dp[i][j] = dp[i-1][j - k]
需要枚举k与j,复杂度n
m*m
发现对于每个j,是被一个区间更新,有如下优化:
但是如果考虑第二维度为前缀和:
那么
dp[i][j] = dp[i-1][j-l] - dp[i - 1][j - r - 1];
代码如下:

#include <bits/stdc++.h>
using namespace std;
const int Mn = 1e5 + 5;
#define ll long long
const ll Mod = 998244353;
ll dp[55][Mn];
ll mib[Mn],prime[Mn];
int cnt = 0,is[Mn];
void inint(){
    
    
    mib[1] = 1;
    for(ll i = 2;i <= Mn - 5;i ++){
    
    
        if(is[i] == 0) prime[++cnt] = i,mib[i] = -1;
        for(int j = 1;j <= cnt && prime[j] * i <= Mn - 5;j ++){
    
    
            is[prime[j] * i] = 1;
            if(i % prime[j] == 0){
    
    
                mib[i * prime[j]] = 0;
                break;
            }
            mib[i * prime[j]] = - mib[i];
        }
    }
}
ll l[55],r[55];
int main(){
    
    
    inint();
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i = 1;i <= n;i ++) scanf("%d%d",l + i,r + i);
    ll ans = 0;
    for(int d = 1;d <= m;d ++){
    
    
        int mx = m / d;
        for(int i = 0;i <= mx;i ++) dp[0][i] = 1;
        for(int i = 1;i <= n;i ++){
    
    
            int L = (l[i] + d  - 1) / d,R = r[i] / d;
            if(L > R){
    
    
                dp[n][mx] = 0;
                break;
            }
            for(int j = 1;j <= mx;j ++){
    
    
                dp[i][j] = dp[i][j - 1];//把1到j-1都继承过来
                //下面是只算和为j的情况,加上上面的1到j-1,就j以内的所有的
                if(j - L >= 0)
                    dp[i][j] += dp[i - 1][j - L];
                dp[i][j] %= Mod;
                if(j - R - 1 >= 0)
                    dp[i][j] -= dp[i - 1][j - R - 1];
                dp[i][j] += Mod;
                dp[i][j] %= Mod;
            }
        }
        ans += (mib[d] * dp[n][mx]);
        ans += Mod;
        ans %= Mod;
    }
    printf("%lld\n",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_45673816/article/details/120358557