T1:最长倍数序列

本题难度中等,先把 \(a\) 从小到大排序。dp[i] 表示以 \(a_i\) 结尾的倍数序列。转移如下:

  • 只有 \(a_i\),对应长度 \(dp[i] = 1\)
  • 上一个数是 \(a_j (1 \leqslant j \leqslant i-1)\),若 \(a_j\)\(a_i\) 的约数,就更新 \(dp[i] = \max(dp[i], dp[j]+1)\)

最终答案就是所有 \(dp\) 的最大值

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)

using namespace std;
using ll = long long;

int main() {
    int n;
    cin >> n;
    
    vector<ll> a(n);
    rep(i, n) cin >> a[i];
    
    sort(a.begin(), a.end());
    
    vector<int> dp(n, 1);
    int maxSize = 1;
    for (int i = 1; i < n; ++i) {
        rep(j, i) {
            if (a[i]%a[j] == 0) {
                dp[i] = max(dp[i], dp[j]+1);
            }
        }
        
        if (dp[i] > maxSize) {
            maxSize = dp[i];
        }
    }
    
    cout << maxSize << '\n';
    
    return 0;
}

T2:大胃王比赛

本题难度较大,随着修行次数增多,成绩一定不会变差,所以可以二分答案。对于成绩 \(x\),计算要使得每名队员的时间都小于等于 \(x\),所需的最小修行次数。对每人计算要让 \(A_iF_i \leqslant x\)\(A_i\) 需要减少的次数。最后判断修行总次数是否小于等于 \(K\)

因为最终成绩是所有人成绩的最大值,所以每次选 \(F\) 最大的人修行的贪心算法是错误的。改成最终成绩是总和就可以变成一个贪心题。

代码实现
#include <bits/stdc++.h>
#define rep(i,n) for (int i = 0; i < (n); ++i)

using namespace std;
using ll = long long;

int main() {
    int n; ll k;
    cin >> n >> k;
    
    vector<int> a(n), f(n);
    rep(i, n) cin >> a[i];
    rep(i, n) cin >> f[i];
    
    ll wa = -1, ac = 1e12;
    while (ac-wa > 1) {
        ll wj = (ac+wa)/2;
        bool ok = [&]{
            ll s = 0;
            rep(i, n) s += max(0ll, a[i]-wj/f[i]);
            return s <= k;
        }();
        (ok ? ac : wa) = wj;
    }
    
    cout << ac << '\n';
    
    return 0;
}