链接: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;
}