Codeforces Round #738 (Div. 2) E.Mocha and Stars 容斥+DP
题意
求满足如下三个条件的序列(a)的个数
1.(forall i (1le ileq 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,可以知道所有数加起来最多贡献(xileq m => x leq lfloorfrac{m}{i} floor)
对于条件1,可以知道每个数最少贡献(lceil frac{a_j}{i} ceil) 最多贡献(lfloorfrac{a_j}{i} floor)
于是可以令(dp[i][j])表示前(i)个数一共贡献(j)个的方案数
[dp[i][j] = sum_{k = L}^Rdp[i-1][k]
]
可以预处理前缀和后(O(nlfloorfrac{m}{i} floor))转移
代码
#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);
}