Codeforces Round #738 (Div. 2) E.Mocha and Stars 容斥+DP
Codeforces Round #738 (Div. 2) E.Mocha and Stars 容斥+DP
题意
求满足如下三个条件的序列\(a\)的个数
1.\(\forall i (1\le i\leq n),a_i \in [l_i,r_i]\)
2.\(\sum_{i=1}^n a_i \leq m\)
3.\(gcd(a_1,a_2...a_n) = 1\)
\[2 \leq n \leq 50,1 \leq m \leq 10^5\\
1 \leq l_i \leq r_i \leq m
\]
分析
首先考虑第3个条件 这类题的通常做法是枚举序列的\(gcd\)
于是有经典的容斥:
\[ans = \sum_{i=1}\mu(i)f(i)
\]
于是此题可以用枚举\(gcd\)处理掉条件3
注意到\(n\)的规模只有50,于是可以对每次进行dp:
若当前枚举的gcd = i,那么每个数可以选择贡献几个\(i\)
对于条件2,可以知道所有数加起来最多贡献\(xi\leq m => x \leq \lfloor\frac{m}{i}\rfloor\)
对于条件1,可以知道每个数最少贡献\(\lceil \frac{a_j}{i}\rceil\) 最多贡献\(\lfloor\frac{a_j}{i}\rfloor\)
于是可以令\(dp[i][j]\)表示前\(i\)个数一共贡献\(j\)个的方案数
\[dp[i][j] = \sum_{k = L}^Rdp[i-1][k]
\]
可以预处理前缀和后\(O(n\lfloor\frac{m}{i}\rfloor)\)转移
代码
#include<bits/stdc++.h>
#define pii pair<ll,ll>
#define fi first
#define se second
using namespace std;
typedef long long ll;
inline ll rd(){
ll x;
scanf("%lld",&x);
return x;
}
const int MOD = 998244353;
inline int mul(int a,int b){
int res = (ll)a * b % MOD;
if(res < 0) res += MOD;
return res;
}
inline void add(int &a,int b){
a += b;
if(a >= MOD) a -= MOD;
}
inline void sub(int &a,int b){
a -= b;
if(a < 0) a += MOD;
}
const int maxn = 1e5 + 5;
int prime[maxn], prime_tot;
int is_prime[maxn];
int mu[maxn];
void pre_calc(int lim) {
mu[1] = 1;
for (int i = 2; i <= lim; i++) {
if (!is_prime[i]) {
prime[++prime_tot] = i;
mu[i] = -1;
}
for (int j = 1; j <= prime_tot; j++) {
if (i * prime[j] > lim) break;
is_prime[i * prime[j]] = 1;
if (i % prime[j] == 0) {
mu[i * prime[j]] = 0;
break;
}
else mu[i * prime[j]] = -mu[i];
}
}
}
int main(){
pre_calc(maxn - 3);
int n = rd();
int m = rd();
vector<int> l(n + 1),r(n + 1);
for(int i = 1;i <= n;i++)
l[i] = rd(),r[i] = rd();
int tot = 0;
for(int i = 1;i <= m;i++){
int lim = m / i;
int suml = 0;
for(int j = 1;j <= n;j++)
suml += (l[j] - 1) / i + 1;
if(suml > lim) continue;
vector<int> dp(lim + 1),sum(lim + 1),v(n + 1);
dp[0] = sum[0] = 1;
for(int j = 1;j <= n;j++){
int L = (l[j] + i - 1) / i;
int R = r[j] / i;
for(int k = 0;k <= lim;k++)
sum[k] = (dp[k] + (k - 1 < 0 ? 0 : sum[k - 1])) % MOD;
for(int k = 0;k <= lim;k++)
dp[k] = (MOD + (k - L < 0 ? 0 : sum[k - L]) - (k - R - 1 < 0 ? 0 : sum[k - R - 1])) % MOD;
//for(int k = 1;k <= lim;k++)
//sum[k] = sum[k - 1] + dp[k];
}
int tmp = 0;
for(int j = 1;j <= lim;j++)
add(tmp,dp[j]);
tot += mul(tmp,mu[i]),tot %= MOD,tot += MOD,tot %= MOD;
}
printf("%d",tot);
}