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;
}
posted @ 2022-07-10 09:21  OIer某罗  阅读(60)  评论(0)    收藏  举报