CF2183E LCM is Legendary Counting Master

有益的题集第十篇。

推式子是一种享受折磨。

题意

你有一个长 \(n\) 的值域在 \([0,m]\) 的整数序列 \(a\)。称 \(a\) 是好的当且仅当它满足以下两个条件:

  • \(a_1<a_2<...<a_n\)
  • \(\frac{1}{\operatorname{lcm}(a_1,a_2)}+\frac{1}{\operatorname{lcm}(a_2,a_3)}+...+\frac{1}{\operatorname{lcm}(a_{n-1},a_n)}+\color{red}\frac{1}{\operatorname{lcm}(a_n,a_1)}\color{black}\ge 1\)

你要将 \(a\) 中所有为 \(0\) 的元素替换为 \([1,m]\) 中的数,并且要使它是好的,求有多少种满足条件的替换方式,答案对 \(998244353\) 取模。

解法

为了方便表述,令 \(a_{n+1}=a_1\)

对于序列严格递增的条件,我们可以先不管,来看第二个条件。容易发现,\(\text{原式}=\sum\limits_{i=1}^{n}\frac{1}{\operatorname{lcm}(a_i,a_{i+1})}=\sum\limits_{i=1}^{n}\frac{\gcd(a_i,a_{i+1})}{a_ia_{i+1}}\)。由于当 \(a_i<a_{i+1}\) 时,\(\gcd(a_i,a_{i+1})\le a_{i+1}-a_i\),于是 \(\sum\limits_{i=1}^{n}\frac{\gcd(a_i,a_{i+1})}{a_ia_{i+1}}\le \sum\limits_{i=1}^{n}\frac{a_{i+1}-a_i}{a_ia_{i+1}}\)

一个小学就学过的数学性质:若 \(a<b\),则 \(\frac{b-a}{ab}=\frac{1}{a}-\frac{1}{b}\),即裂项。所以

\[\sum\limits_{i=1}^{n-1}\frac{a_{i+1}-a_i}{a_ia_{i+1}}=\sum\limits_{i=1}^{n-1}\frac{1}{a_{i}}-\frac{1}{a_{i+1}}=\frac{1}{a_1}-\frac{1}{a_{n}} \]

为什么求和上界是 \(n-1\) 呢,是因为 \(a_{n+1}=a_1<a_n\),不满足裂项条件。那么得到

\[\sum\limits_{i=1}^{n}\frac{a_{i+1}-a_i}{a_ia_{i+1}}=\frac{1}{a_1}-\frac{1}{a_{n}}+\frac{\gcd(a_1,a_{n})}{a_1a_{n}} \]

显然 \(\gcd(a,b)\le\min\{a,b\}\),所以

\[\sum\limits_{i=1}^{n}\frac{\gcd(a_i,a_{i+1})}{a_ia_{i+1}}\le\frac{1}{a_1}-\frac{1}{a_{n}}+\frac{\gcd(a_1,a_{n})}{a_1a_{n}}\le\frac{1}{a_1}-\frac{1}{a_{n}}+\frac{a_1}{a_1a_{n}}=\frac{1}{a_1}-\frac{1}{a_{n}}+\frac{1}{a_{n}}=\frac{1}{a_1}\le1 \]

哇,我们竟然得到了 \(\frac{1}{\operatorname{lcm}(a_1,a_2)}+\frac{1}{\operatorname{lcm}(a_2,a_3)}+...+\frac{1}{\operatorname{lcm}(a_{n-1},a_n)}+\frac{1}{\operatorname{lcm}(a_n,a_1)}\le1\)!而题目要求那一坨式子大于等于 \(1\),所以最终原式必须等于 \(1\)

现在考虑什么时候那个式子能够取等。如果原式要等于 \(1\),则必须有 \(\boldsymbol{a_1=1}\),并且需要满足 \(\gcd(a_i,a_{i+1})=a_{i+1}-a_i\)。这也就是说,\(a_{i+1}-a_i\)\(a_i\) 的因数,即存在正整数 \(d\) 使 \(d\mid a_i\)\(a_{i+1}=a_i+d\)百度一下可以发现,这样的二元组 \((a_i,a_{i+1})\) 不会超过 \(O(m\log m)\) 个。

考虑 dp。设 \(f_{i,j}\) 表示当前已经替换到了第 \(i\) 个数,将要换上去的数是 \(j\) 的总方案数。根据上面的推导,我们可以先预处理出在数据范围内的所有二元组 \((a_i,a_{i+1})\)。初始时 \(f_{1,1}=1\)。可以得到转移方程

\[f_{i,j}=\sum_{j=1}^{m}f_{i-1,j-x} \]

其中 \(x\) 是能够与 \(j\) 组成那个二元组的所有数。

最终的答案为 \(\sum\limits_{i=1}^{m}f_{n,i}\)

注意,因为有一些数是已经填好了的(即 \(a_i\not=0\)),所以这些数只能算它们自己本身的值的贡献,所以如果当前 \(j\not=a_i\) 的话要将这部分贡献清零。

记得取模。

Code
#include <bits/stdc++.h>
#define loop(i,a,b) for(int i=(a);~i;i=(b))
#define Mem(a,b) memset ((a),(b),sizeof((a)))
#define eb emplace_back
#define pb push_back
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
constexpr int N = 3000 + 15, mod = 998244353;

namespace FAST_IO {
//快读快写已为您省略
}
using namespace FAST_IO;

bool MS;

int n, m;
int a[N];
ll dp[N][N];
vector <int> d[N];

void solve () {
	read (n, m);
	for (int i = 1; i <= n; ++ i) read (a[i]);
	if (a[1] > 1) {
		print (0, '\n');
		return ;
	}//a1=1才能进行计算,因为这是前提
	dp[1][1] = 1;
	for (int i = 2; i <= n; ++ i) {
		for (int j = 1; j <= m; ++ j) {
			dp[i][j] = 0;
			for (int x : d[j]) (dp[i][j] += dp[i - 1][j - x]) %= mod;
		}//计算贡献
		if (a[i]) for (int j = 1; j <= m; ++ j) if (j ^ a[i]) dp[i][j] = 0;//去掉不合法的贡献
	}//转移
	ll ans = 0;
	for (int j = 1; j <= m; ++ j) {
		(ans += dp[n][j]) %= mod;
	}
	print (ans, '\n');
}

bool MT;

int main () {
	for (int i = 1; i <= N - 15; ++ i) {
		for (int j = i; j <= N - 15; j += i) {
			d[j].eb (i);
		}
	}//预处理
	int T_T;
	read (T_T);
	while (T_T --) solve ();
	cerr << "Memory:" << (&MT - &MS) / 1048576.0 << "MB Time:" << clock() << "ms\n";
	return 0;
}
posted @ 2026-01-10 13:48  XXh_Laoxu  阅读(7)  评论(0)    收藏  举报

转载请注明出处!


#页面摧毁游戏#
使用【上下左右】控制飞行器的运动
使用【空格】发射导弹
点击开始摧毁