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\)的限制:

\[\sum_{a_1 = l_1}^{r_1}\cdots\sum_{a_n=l_n}^{r_n}[\sum_{i = 1}^na_i \leq m][gcd(a_1,a_2,\cdots,a_n) == 1] \]

后面的 \(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)\),即原式变为:

\[\sum_{a_1 = l_1}^{r_1}\cdots\sum_{a_n=l_n}^{r_n}[\sum_{i = 1}^na_i \leq m]\sum_{d|gcd(a_1,\cdots,a_n) }\mu(d) \]

\[\sum_{a_1 = l_1}^{r_1}\cdots\sum_{a_n=l_n}^{r_n}[\sum_{i = 1}^na_i \leq m]\sum_{d|a_1 \&\& \cdots \&\& d|a_n}\mu(d) \]

首先枚举 \(d\)则变为:

\[\sum_{d = 1}^m \mu(d) \sum_{a_1 = \lceil \frac {l_1}d \rceil}^{\lfloor \frac {r_1}d \rfloor} \cdots \sum_{a_n = \lceil \frac {l_n}d \rceil}^{\lfloor \frac {r_n}d \rfloor} [\sum_{i=1}^n a_i <= \frac md] \]

中间\(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;
}
posted @ 2021-09-05 13:36  !^^!  阅读(66)  评论(0编辑  收藏  举报