E. Mocha and Stars —— Codeforces Round #738 (Div. 2)
E. Mocha and Stars
https://codeforces.com/contest/1559/problem/E
题目描述
题目给出一个长度为\(n\)的序列的定义:
\(\sum_{i = 1}^na_i <= m\)
\(gcd(a_1,\cdots,a_n) = 1\)
同时给出 \(n\)个区间\([l_i,r_i]\),并且对于每个\(a_i\)只能在给出的\([l_i,r_i]\)中取值,询问序列\(a\)的所有可能数量。
解题思路
假设没有\(gcd\)的限制,则此题变成了二维背包问题,即从\(n\)个范围中取数满足 \(\sum_{i=1}^n a_i <= m\),求解方法如下:
for(int i = 1; i <= n; i++){
for(int j = l[i]; j <= r[i]; j++){
for(int k = j; k <= m; k++){
dp[i][k] += dp[i][k-j];
}
}
}
即:\(\sum_{i = 1}^n\sum_{j=l[i]}^{r[i]}\sum_{k=j}^mdp[i][k-j]\),观察此式可以看出对于每个 \(k\),\(dp[i][k] = \sum_{j = l[i]}^{r[i]}dp[i][k-j] = \sum_{j = max(1,j-r[i])}^{j-l[i]}dp[i][j]\),所以后面这个式子就可以用前缀和优化,即:\(dp[i][k-l[i]] - dp[i][k-r[i]-1]\)
下面再看 \(gcd\)的限制:
后面的 \(gcd(a_1,a_2,\cdots,a_n) == 1\) 等价于 \(\epsilon(gcd(a_1,\cdots,a_n))\),即:\(\sum_{d|gcd(a_1,\cdots,a_n) }\mu(d)\),即原式变为:
首先枚举 \(d\)则变为:
中间\(n\)个和式使用了容斥定理,因为\(d\)能整除\(i\),则\(i\)一定是\(kd,k \in \{1,2,\cdots\}\),故 \(\sum_l^r\)变成了 \(\sum_{i = 0}^{\lfloor \frac rd \rfloor} - \sum_{i = 0}^{\lfloor \frac ld \rfloor}\),即是: \(\sum_{\lceil \frac ld \rceil}^{\lfloor \frac rd \rfloor}\),然后最后判断的式子 \([\sum_{i=1}^n a_i <= \frac md]\)可以用背包加前缀和优化,和上面类似。
注:\(sum[i][j]\)表示背包的前缀和,\(f[i][j]\)表示第\(i\)维的背包,每次需要更新第\(i\)维的前缀和 \(sum\)
Code
#include <bits/stdc++.h>
#define ll long long
#define qc ios::sync_with_stdio(false); cin.tie(0);cout.tie(0)
#define fi first
#define se second
#define PII pair<int, int>
#define PLL pair<ll, ll>
#define pb push_back
#define V vector
using namespace std;
const int N = 2e5 + 7;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const ll mod = 998244353;
inline int read()
{
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
return x*f;
}
int prime[N],prime_tot;
bool prime_tag[N];
int mu[N];
void pre(){
mu[1] = 1;
for(int i = 2; i <= N; i++){
if(!prime_tag[i]){
prime[++prime_tot] = i;
mu[i] = -1;
}
for(int j = 1; j <= prime_tot; j++){
if(i * prime[j] > N)break;
prime_tag[i * prime[j]] = true;
if(i % prime[j] == 0){
mu[i * prime[j]] = 0;
break;
}else{
mu[i * prime[j]] = -mu[i];
}
}
}
}
ll ans;
ll f[57][N],sum[57][N];
PII a[57];
void solve(){
pre();
int n,m;
cin >> n >> m;
for(int i = 1; i <= n; i++)
cin >> a[i].fi >> a[i].se;
for(int d = 1; d <= m; d++){
int up = m / d;
for(int i = 0; i <= up; i++)
sum[0][i] = 1;
for(int i = 1; i <= n; i++){
int l = (a[i].fi + d - 1) / d, r = a[i].se / d;
for(int j = l; j <= up; j++){
f[i][j] = sum[i-1][j-l];
if(j >= r + 1)f[i][j] = (f[i][j] - sum[i-1][j-r-1] + mod) % mod;
}
for(int j = 1; j <= up;j ++)
sum[i][j] = (sum[i][j-1]+f[i][j]) % mod;
}
ans = (ans + ((sum[n][up] * mu[d] + mod) % mod)) % mod;
}
cout << ans << "\n";
}
int main()
{
#ifdef ONLINE_JUDGE
#else
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
qc;
int T = 1;
//cin >> T;
while(T--){
solve();
}
return 0;
}