注意到这里有个区间的 \(b_i\) 最小。我们考虑每个 \(b_i\) 作为最小的时候各操作几次。显然每个 \(b_i\) 的【操作区间】更长是不劣的。于是这些个 \(b_i\) 是可以打成笛卡尔树,进行 DP。
想到这一点,DP 便是不难的了。
定义 \(f_{i, j}\) 为以 \(i\) 为根的子树内,最优操作后最大值为 \(j\) 的最少操作次数。但是状态数很多,而答案的值域很少(每次都至少折半,顶多 \(60\) 多次)。有个很典的套路是将状态的一维与答案对调。
于是定义新的 \(f_{i, j}\) 为以 \(i\) 为根的子树内,操作 \(j\) 次后得到的最优的最大值。答案统计就是最小的 \(j\),满足 \(f_{root, j} = 1\)。
DP 过程就是一个背包合并。DP 的过程总先合并儿子,再将本身统计进去,可以巧妙地解决 \(b_i\) 要算进整个区间的小事情。
注意 \(\min,\max\) 的细节。
时间复杂度 \(O(n \log ^2 V)\)。
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
#define Linf 0x3f3f3f3f3f3f3f3f
#define pii pair<int, int>
#define all(v) v.begin(), v.end()
#define int long long
using namespace std;
//#define filename "xxx"
#define FileOperations() freopen(filename".in", "r", stdin), freopen(filename".out", "w", stdout)
#define multi_cases 1
namespace Traveller {
const int N = 2e5+2;
int n, a[N], b[N];
int root, l[N], r[N];
stack<int> st;
int f[N][62];
void solve(int u) {
if(u == 0) return;
if(l[u] == 0 && r[u] == 0) {
f[u][0] = a[u];
for(int i = 1; i < 62; ++i) f[u][i] = f[u][i-1] / b[u];
return;
}
solve(l[u]), solve(r[u]);
for(int i = 0; i < 62; ++i)
for(int j = 0; j <= i; ++j)
f[u][i] = min(f[u][i], max(a[u], max(f[l[u]][j], f[r[u]][i-j])));
for(int i = 1; i < 62; ++i)
f[u][i] = min(f[u][i], f[u][i-1] / b[u]);
}
void main() {
scanf("%lld", &n);
for(int i = 1; i <= n; ++i) scanf("%lld", &a[i]), --a[i];
for(int i = 1; i <= n; ++i) scanf("%lld", &b[i]);
for(int i = 1; i <= n; ++i) l[i] = r[i] = 0;
while(!st.empty()) st.pop();
b[root = 0] = Linf;
for(int i = 1; i <= n; ++i) {
while(!st.empty() && b[i] < b[st.top()]) l[i] = st.top(), st.pop();
if(!st.empty()) r[st.top()] = i;
st.push(i);
if(b[i] < b[root]) root = i;
}
for(int i = 1; i <= n; ++i)
for(int j = 0; j < 62; ++j) f[i][j] = Linf;
solve(root);
for(int i = 0; i < 62; ++i)
if(f[root][i] == 0) {
printf("%lld\n", i);
return;
}
}
}
signed main() {
#ifdef filename
FileOperations();
#endif
signed _ = 1;
#ifdef multi_cases
scanf("%d", &_);
#endif
while(_--) Traveller::main();
return 0;
}
闲话:一开始写了很__的做法,结果 T 飞。后来抄题解抄了这种做法。时间复杂度相同,由于(笛卡尔树的某种性质?数据太弱?)神秘原因,快了许多。950 多 ms 直接飘过。正解应该是要用一下单调性,合并的时候上点手法消掉一只 \(\log\)。
标签:CF2048F,int,题解,filename,62,Math,DP,define From: https://www.cnblogs.com/wfc284/p/18677624