题解:CF2048F Kevin and Math Class

题意:很简单了,不再赘述。

做法:

首先我们注意到,因为 \(b_i\ge 2\),所以我们无脑全局操作 \(\log V\) 次之后一定可以满足条件。

同时注意到我们是对区间中最小的 \(b\) 以他为除数,所以说我们很自然可以去建出一个 \(b\) 的小根笛卡尔树。

发现对于一个笛卡尔树的子树,我们肯定是直接全部一起操作若干次,两个子树间再分别操作是最优的。

因为操作次数非常少,所以我们考虑直接 dp,\(dp_{u,j}\) 代表 \(u\) 的子树内,操作 \(j\) 次可以得到的答案。转移时,我们先枚举子树中的操作,显然是一个背包的转移方式,记得对 \(a_u\)\(\max\)。合并完之后我们考虑对当前点 \(u\) 操作,那么就直接从前往后令 \(dp_{u,j}\)\(\lceil\frac{dp_{u,j-1}}{b_u}\rceil\)\(\min\) 即可。

复杂度 \(O(n\log^2n)\)

代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int maxn = 2e5 + 5, mx = 64;
int n, a[maxn], b[maxn], dp[maxn][70], st[maxn][21], lg[maxn];
int getmax(int x, int y) {
	return (b[x] < b[y] ? x : y);
}
void prepare() {
	lg[0] = -1;
	for (int i = 1; i <= n; i++)
		lg[i] = lg[i >> 1] + 1;
	for (int j = 1; (1 << j) <= n; j++)
		for (int i = 1; i + (1 << j) - 1 <= n; i++)
			st[i][j] = getmax(st[i][j - 1], st[i + (1 << j - 1)][j - 1]);	
}
int query(int l, int r) {
	int k = lg[r - l + 1];
	return getmax(st[l][k], st[r - (1 << k) + 1][k]);
}
int solve(int l, int r) {
	if(l > r)
		return 0;
	if(l == r) {
		dp[l][0] = a[l];
		for (int i = 1; i <= mx; i++)
			dp[l][i] = (dp[l][i - 1] + b[l] - 1) / b[l];
		return l;
	}	
	int mid = query(l, r);
	int ls = solve(l, mid - 1), rs = solve(mid + 1, r);
//	cout << dp[ls][0] << " " << dp[rs][0] << " " << l << " " << r << " " << mid << endl;
	for (int i = 0; i <= mx; i++)
		for (int j = 0; j <= mx; j++) {
			if(i + j <= mx)
				dp[mid][i + j] = min(dp[mid][i + j], max(dp[ls][i], max(dp[rs][j], a[mid])));
		}
	for (int i = 1; i <= mx; i++)
		dp[mid][i] = min(dp[mid][i], dp[mid][i - 1]);
	for (int i = 1; i <= mx; i++)
		dp[mid][i] = min(dp[mid][i], (dp[mid][i - 1] + b[mid] - 1) / b[mid]);
	return mid;
}
void solve() {
	cin >> n;
	for (int i = 1; i <= n; i++)
		cin >> a[i];
	for (int i = 1; i <= n; i++)
		cin >> b[i], st[i][0] = i;
	prepare();
	for (int i = 1; i <= n; i++)
		for (int j = 0; j <= mx; j++)
			dp[i][j] = 9e18;
	int rt = solve(1, n);
	for (int i = 0; i <= mx; i++) {
		if(dp[rt][i] <= 1) {
			cout << i << endl;
			return ;
		}
	}
}
signed  main() {
	int T;
	cin >> T;
	while(T--)
		solve();
	return 0;
}
posted @ 2025-08-11 22:27  LUlululu1616  阅读(14)  评论(0)    收藏  举报