Codeforces Round #738 (Div. 2) E.Mocha and Stars 容斥+DP

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);
}
原文地址:https://www.cnblogs.com/hznumqf/p/15150187.html