1 条题解
-
0
题目分析
问题重述
我们需要找到最小的非负整数 ,使得存在 天的调整 满足:
关键观察
设:
- (调整量的x总和)
- (调整量的y总和)
- (固定风力的x总和)
- (固定风力的y总和)
则条件可重写为:
- 每天调整量满足曼哈顿距离约束
解法思路
数学推导
设 ,其中 ,则:
$$T_x = q \cdot \sum_{i=0}^{n-1} x_i + \sum_{i=0}^{r-1} x_i $$$$T_y = q \cdot \sum_{i=0}^{n-1} y_i + \sum_{i=0}^{r-1} y_i $$令:
则:
我们需要:
调整量的约束
由于每天调整量满足 ,那么 天的总调整量满足:
因此必要条件为:
$$|x - qX - X_r| + |y - qY - Y_r| \leq mk = k(qn + r) $$算法框架
对于每个余数 (),我们需要找到最小的 使得:
然后取所有 组合中的最小值 。
代码实现
#include <bits/stdc++.h> using namespace std; typedef long long ll; const ll INF = 1e18; ll n, k, x, y; vector<ll> xs, ys; vector<ll> preX, preY; // 前缀和 // 检查对于给定的q和r是否满足条件 bool check(ll q, ll r) { ll total_days = q * n + r; ll total_X = q * preX[n] + preX[r]; // T_x ll total_Y = q * preY[n] + preY[r]; // T_y ll S_x = x - total_X; // 需要的调整量x总和 ll S_y = y - total_Y; // 需要的调整量y总和 // 检查曼哈顿距离约束 return abs(S_x) + abs(S_y) <= k * total_days; } ll solve() { cin >> n >> k >> x >> y; xs.resize(n); ys.resize(n); for (int i = 0; i < n; i++) { cin >> xs[i] >> ys[i]; } // 计算前缀和 preX.resize(n + 1); preY.resize(n + 1); preX[0] = preY[0] = 0; for (int i = 0; i < n; i++) { preX[i + 1] = preX[i] + xs[i]; preY[i + 1] = preY[i] + ys[i]; } ll total_X = preX[n]; // X ll total_Y = preY[n]; // Y ll ans = INF; // 枚举余数r for (ll r = 0; r <= n; r++) { // 二分查找最小的q ll left = 0, right = 2e8; // 设置足够大的上界 ll best_q = INF; while (left <= right) { ll mid = (left + right) / 2; if (check(mid, r)) { best_q = mid; right = mid - 1; } else { left = mid + 1; } } if (best_q != INF) { ans = min(ans, best_q * n + r); } } return ans == INF ? -1 : ans; } int main() { ios::sync_with_stdio(false); cin.tie(0); int T; cin >> T; while (T--) { cout << solve() << "\n"; } return 0; }优化分析
上述代码对于每个测试数据是 的,其中 是 的上界。但 最大为 , 最大为 ,总 为 ,可能仍然会超时。
数学优化
观察不等式:
这可以重写为:
其中 , ,
对于固定的 ,这是一个关于 的不等式。我们可以分析函数 的行为。
更高效的实现
#include <bits/stdc++.h> using namespace std; typedef long long ll; const ll INF = 4e18; // 需要更大的上界 ll n, k, x, y; vector<ll> xs, ys; vector<ll> preX, preY; // 计算对于给定q和r的曼哈顿距离 ll calc_manhattan(ll q, ll r) { ll total_X = q * preX[n] + preX[r]; ll total_Y = q * preY[n] + preY[r]; return abs(x - total_X) + abs(y - total_Y); } ll solve_optimized() { cin >> n >> k >> x >> y; xs.resize(n); ys.resize(n); for (int i = 0; i < n; i++) { cin >> xs[i] >> ys[i]; } preX.resize(n + 1); preY.resize(n + 1); preX[0] = preY[0] = 0; for (int i = 0; i < n; i++) { preX[i + 1] = preX[i] + xs[i]; preY[i + 1] = preY[i] + ys[i]; } ll total_X = preX[n]; ll total_Y = preY[n]; ll ans = INF; // 特殊情况:m = 0 if (x == 0 && y == 0) { ans = 0; } // 枚举余数r for (ll r = 0; r <= n; r++) { if (r == n) r = 0; // r从0到n-1 ll base_manhattan = abs(x - preX[r]) + abs(y - preY[r]); // 如果总风量为0,需要特殊处理 if (total_X == 0 && total_Y == 0) { if (base_manhattan <= k * r) { ans = min(ans, r); } continue; } // 二分查找q ll left = 0, right = 4e8; while (left <= right) { ll mid = left + (right - left) / 2; ll manhattan_dist = calc_manhattan(mid, r); ll total_days = mid * n + r; if (manhattan_dist <= k * total_days) { ans = min(ans, total_days); right = mid - 1; } else { left = mid + 1; } } } return ans == INF ? -1 : ans; } int main() { ios::sync_with_stdio(false); cin.tie(0); int T; cin >> T; while (T--) { cout << solve_optimized() << "\n"; } return 0; }特殊情况处理
- :当 且 时,答案可能是0
- 总风量为0:当 且 时,问题简化为检查单个周期
- :此时每天调整量必须为0,问题有特殊性质
复杂度分析
- 时间复杂度: 每个测试数据,其中 是 的上界
- 空间复杂度:
- 总复杂度:,在约束范围内
算法总结
- 问题转化:将原问题转化为寻找满足不等式的最小
- 周期分解:将 分解为 的形式
- 二分答案:对于每个余数 ,二分查找最小的
- 曼哈顿距离:使用绝对值不等式作为约束条件
这道题的关键在于将复杂的约束条件转化为数学不等式,然后利用二分查找高效求解。
- 1
信息
- ID
- 5570
- 时间
- 5000ms
- 内存
- 512MiB
- 难度
- 8
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者