题解:CF2048F Kevin and Math Class

Problem

statement

给定长度为 \(n\) 的数组 \(a,b\),每次操作可以任意选择一个区间 \([l,r]\),记 \(x = \displaystyle\min_{l \leq i \leq r}b_i\),然后 \(\forall a_i(i \in [l,r]),a_i \leftarrow \lfloor{\frac{a_i}{x}}\rfloor\),求最终使得 \(a_i\) 均变为 \(1\) 的最小操作次数。

solution

补题听学长讲的。

首先观察数据范围,发现 \(b_i\) 下界为 \(2\),意味着每次操作选取整段,最多 \(\log a_i\) 此操作,约为 \(60\),十分可做。

在我们钦定 \(b_i\) 为一个包含它的区间的最小值时,选取一段极长区间并不会对答案产生劣的影响,也就是说,选取区间存在包含性,因此考虑建笛卡尔树。

\(dp_{i,j}\) 表示 \(i\) 号点值为 \(j\) 的最小操作次数,由于值域 \(V \leq 10^{18}\),考虑将第二维与定义交换,即 \(dp_{i,j}\) 表示 \(i\) 节点在最小操作次数为 \(j\) 时的值,转移如下:

\[\begin{aligned} tmp &\leftarrow \max\{a_u,\max\{dp_{u,j},dp_{v,k}\}\} \\ dp_{u,j + k} &\leftarrow \min\{dp_{i,j + k},tmp\} \\ dp_{u,j} &\leftarrow \min\{dp_{i,j},\lceil{\frac{dp_{u,j - 1}}{b_u}}\rceil\} \end{aligned} \]

其中 \(u,v\) 分别表示子树根的左右儿子。

答案为 \(dp_{root,i} = 1\) 时的 \(i\)

code

#include <bits/stdc++.h>
#define int long long

using namespace std;
inline void read (int &x) {
    int res = 0, f = 1;
    char ch = getchar();
    while (!isdigit (ch)) f = ch == '-' ? -1 : 1, ch = getchar();
    while (isdigit (ch)) res = (res << 1)  + (res << 3) + (ch ^ 48), ch = getchar();
    x = res * f;
}

const int maxn = 2e5 + 10, inf = 0x7f7f7f7f7f7f7f7f;
int T, n, a[maxn], b[maxn], st[maxn], top, dp[maxn][66];
struct Ayaka { int lson, rson; } tree[maxn << 1];

inline void init() {
    for (int i = 0; i <= 60; i ++) dp[0][i] = 0;
    memset (st, 0, sizeof(st)), top = 0;
    for (int i = 0; i <= n; i ++) tree[i].lson = tree[i].rson = 0;
}

void Dp (int rt) {
    if (!rt) return;
    Dp (tree[rt].lson), Dp (tree[rt].rson);
    int i = 0, j = 0, l = tree[rt].lson, r = tree[rt].rson;
    for (int k = 0; k <= 60; k ++) dp[rt][k] = inf;
    while (i + j <= 60) {
        int tmp = max (a[rt], max(dp[l][i], dp[r][j]));
        dp[rt][i + j] = min (dp[rt][i + j], tmp);
        if (dp[l][i] > dp[r][j]) i ++;
        else j ++;
    }
    for (int i = 1; i <= 60; i ++)
        dp[rt][i] = min (dp[rt][i], (dp[rt][i - 1] - 1) / b[rt] + 1);
}

inline void Solve() {
    init();
    read (n);
    for (int i = 1; i <= n; i ++) read (a[i]);
    for (int i = 1; i <= n; i ++) read (b[i]);
    for (int i = 1; i <= n; i ++) {
        while (top && b[st[top]] > b[i]) top --;
        tree[i].lson = tree[st[top]].rson;
        tree[st[top]].rson = i;
        st[++ top] = i;
    }
    Dp (st[1]);
    int Anspos = 0;
    for (int i = 0; i <= 60; i ++) {
        if (dp[st[1]][i] == 1) {
            Anspos = i;
            break;
        }
    }
    printf ("%lld\n", Anspos);
    return;
}

signed main() {
    read (T);
    while (T --)
        Solve();
    return 0;
}
posted @ 2024-12-23 14:35  Alec_Ayaka  阅读(6)  评论(0编辑  收藏  举报