CF1699E Three Days Grace 解题报告
题目大意:给定 \(n\) 个 \(1 \sim m\) 的数,可以将任意一个数分解成若干个数使得它们的乘积是这个数。求最后分解的数中最大数和最小数之差的最小值。
\(\sum n \le 10^6,\sum m \le 5 \times 10^6\)
分析:考虑 DP,设 \(dp[i][j]\) 表示 \(j\) 分解成一些数字使得这些数字不小于 \(i\),这些数字的最大值最小是多少。考虑转移: \(dp[i][j] = min(dp[i + 1][j], if(i | j \&\& i < j / i)dp[i][j / i])\)
外层枚举 \(i\),内层枚举 \(j\),时间复杂度 \(O(n^2)\)。
发现可以滚掉 \(i\) 这个维度,只转移 \(j/i\),那么我们就可以对于每个 \(i\) 只枚举 \(j \in [i \times i, \max{a_k}] \&\& i | j\),这个复杂度是 \(O(i \times \ln m)\) 的(调和级数)
注意到需要维护 \(\max\{dp[i][a_k]\}\),且 \(\max\) 的值随着 \(i\) 的下降单调不增。考虑双指针法。
#include<bits/stdc++.h>
using namespace std;
#define f(i, a, b) for(int i = (a); i <= (b); i++)
#define cl(i, n) i.clear(),i.resize(n);
#define endl '\n'
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
const int inf = 1e9;
ll n, m;
ll a[1000010];
vector<bool> cx;
vector<ll> dp;
vector<ll> tong;
int main() {
ios::sync_with_stdio(0);
cin.tie(NULL);
cout.tie(NULL);
int t; cin >> t;
while(t--) {
cin >> n >> m;
cl(cx, m + 10);
cl(tong, m + 10);
ll ans = inf;
ll mn = inf, mx = 0;
f(i, 1, n) {
cin >> a[i];
mn = min(mn, a[i]); mx = max(mx, a[i]);
cx[a[i]]=1;
}
cl(dp, m + 10);
f(i,0,mx)dp[i]=i;
f(i, 1,n) tong[a[i]] = 1;
int p = mx;
for(ll i = mx; i >= 1; i--) {
for(ll j = i * i; j <= mx; j += i) {
if(cx[j]) tong[dp[j]]--;
dp[j] = min(dp[j],dp[j/i]);
if(cx[j]) tong[dp[j]]++;
}
while(!tong[p]) p--;
if(i <= mn)
ans = min(ans, p - i);
}
cout << ans << endl;
}
return 0;
}

浙公网安备 33010602011771号