Loading

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);
}
posted @ 2021-08-16 22:10  MQFLLY  阅读(131)  评论(0编辑  收藏  举报