与季风大战 1.5h,结果快读写挂了第一发提交爆零,怎么会是呢/ll。
为方便描述和代码实现,下文默认 \(x_i\) 从 \(1\) 到 \(n\) 编号,而不是从 \(0\) 到 \(n - 1\) 编号。
题目要我们求满足条件的 \(m\) 的最小值,不妨看看它给我们的式子可以推出什么约束。
首先我们发现前两个式子把未知的 \(x_i'\),\(y_i'\) 和已知的 \(x_i\),\(y_i\) 混到一起了。这对于想要推出 \(x_i'\) 和 \(y_i'\) 的约束的我们来说很不好。于是显然可以考虑移项,得到:
-
\(\sum\limits_{i = 1}^{m} x_i' = x - \sum\limits_{i = 1}^{m} x_{i \bmod n}\)
-
\(\sum\limits_{i = 1}^{m} y_i' = y - \sum\limits_{i = 1}^{m} y_{i \bmod n}\)
考虑第三个式子,它直接对 \(x_i'\) 和 \(y_i'\) 进行了约束。但是我们显然不能直接构造出 \(x_i'\) 和 \(y_i\) 并判断它们是否符合约束,于是考虑把第三个式子和前两个式子联系起来,也就是把它化成 \(\sum\limits_{i = 1}^{m} x_i'\) 和 \(\sum\limits_{i = 1}^{m} y_i'\) 的形式。
直观感受一下,容易发现,设我们通过前两个式子得到了 \(\sum x_i'\) 和 \(\sum y_i'\) 的取值范围,那么为了使它们也满足第三个式子,我们肯定是会把 \(x_i'\) 变得尽量平均且两两同号,对于 \(y_i'\) 同理。
因此,容易得到,第三个式子无法被满足,当且仅当 \(\sum\limits_{i = 1}^{m} x_i' + \sum\limits_{i = 1}^{m} y_i' > mk\)。充分性显然,必要性只需要考虑将 \(i\) 较小的时候的 \(x_i' + y_i'\) 设定为 \(k\) 即可。
但是这时,前两个式子的右半部分也是可变的。这个的解决方式比较容易,考虑枚举 \(m \bmod n\) 的值并每次 \(O(1) / O(\log)\) 地处理即可。也就是说,设 \(w = m \bmod n\),则 \(w\) 确定时,我们可以把这三个式子整理为一个约束:
这个式子中只有 \(t\) 是不确定的,它代表 \(\left\lfloor \frac{m}{n} \right\rfloor\)。
这个式子的左边有两个绝对值,根据初中数学,我们可以直接把它拆成四个式子,并求符合范围的值即可。
该做法的时间、空间复杂度均为 \(O(n)\),实测不卡常、不使用 __int128
、不使用快读而使用 cin / cout
并使用 endl
也能过。代码很短,只有不到 \(1.5 \text{kb}\)。
#include <bits/stdc++.h>
using namespace std;
#define int long longconst int N = 1e5 + 5, INF = 1e18;int T, n, k, x, y, min_val, max_val;
int a[N], b[N], pre1[N], pre2[N];int Div1(int p, int q) {if (p % q == 0 || (p > 0 && q < 0) || (p < 0 && q > 0)) return p / q;else return p / q + 1;
}int Div2(int p, int q) {if (p % q == 0 || (p > 0 && q > 0) || (p < 0 && q < 0)) return p / q;else return p / q - 1;
}void get(int xcur, int p, int ycur, int q, int sum, int val) {int tmp1 = sum - p - q, tmp2 = xcur + ycur - val;if (tmp1 > 0 && min_val <= INF) min_val = max(min_val, Div1(tmp2, tmp1));else if (tmp1 < 0) max_val = min(max_val, Div2(tmp2, tmp1));else if (tmp2 > 0) min_val = INF + 1;else return;
}void Solve() {int ans = INF + 1;cin >> n >> k >> x >> y;for (int i = 1; i <= n; ++i) cin >> a[i] >> b[i];for (int i = 1; i <= n; ++i) pre1[i] = pre1[i - 1] + a[i], pre2[i] = pre2[i - 1] + b[i];for (int i = 0; i <= n; ++i) {max_val = INF; min_val = 0;get(x - pre1[i], -pre1[n], y - pre2[i], -pre2[n], n * k, i * k);get(x - pre1[i], -pre1[n], pre2[i] - y, pre2[n], n * k, i * k);get(pre1[i] - x, pre1[n], y - pre2[i], -pre2[n], n * k, i * k);get(pre1[i] - x, pre1[n], pre2[i] - y, pre2[n], n * k, i * k);if (max_val >= min_val) ans = min(ans, min_val * n + i);}if (ans == INF + 1) ans = -1;cout << ans << endl;
}signed main() {cin >> T;while (T--) Solve();return 0;
}