Description
选择尽量少的模长不超过
的整向量,使得他们的和为
.
Difficulty
MainAlgorithm
凸包
Minkowski 和
Complexity
Solution
首先,跳跃一步能跳跃到的点是一个凸包内的整点。
然后,由于每一步是相同的,可以证明第
次跳跃产生的图形是第
次跳跃产生的图形与第
次跳跃产生的图形的 Minkowski 和。
由于是同一个凸包产生的 Minkowski 和,可以很容易地证明第
次跳跃产生的凸包与第一次跳跃产生的凸包相似。
那么,对于在第一个凸包上出现的点
,其在第
次跳跃的凸包中为
.
故我们只需要一开始用
的时间把凸包求出来,然后在凸包上找到
对应的覆盖边,把
求出来即可。注意在求凸包时不能直接保留所有点,要用叉积去除凹点。
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#define Rep(i, x, y) for (int i = x; i <= y; i ++)
#define Dwn(i, x, y) for (int i = x; i >= y; i --)
#define RepE(i, x) for(int i = pos[x]; i; i = g[i].nex)
using namespace std;
typedef long long LL;
const int N = 100005;
class ToastJumping {
public:
vector<int> ans; LL px[N], py[N], q;
LL Cross(LL x, LL y, LL x1, LL y1, LL x2, LL y2) {
return (x1 - x) * (y2 - y) - (x2 - x) * (y1 - y);
}
vector <int> minJumps(vector <int> X, vector <int> Y, vector <int> D) {
int T = X.size();
Rep(t0, 0, T - 1) {
LL n = abs(X[t0]), m = abs(Y[t0]), d = abs(D[t0]);
q = 0;
for (LL i = 0; i * i <= d; i ++) {
int j = sqrt(d - i * i);
if (i > j) break ;
px[++ q] = i, py[q] = j;
while (q > 2 && Cross(px[q - 2], py[q - 2], px[q - 1], py[q - 1], px[q], py[q]) > 0) q --, px[q] = px[q + 1], py[q] = py[q + 1];
}
Dwn(i, q, 1) px[2 * q - i + 1] = py[i], py[2 * q - i + 1] = px[i]; q *= 2;
if (n == 0 && m == 0) { ans.push_back(0); continue ; }
if (n == 0 || m == 0) { int r = sqrt(d); ans.push_back((n + m - 1) / r + 1); continue ; };
int u = 0;
Rep(i, 1, q) if (Cross(0, 0, px[i], py[i], n, m) > 0) { u = i; break ; }
u --;
LL l = 1, r = 2;
if (px[u + 1]) r += n / px[u + 1]; if (py[u]) r += m / py[u]; r = min(r, n + m);
LL x = px[u], y = py[u], x2 = px[u + 1], y2 = py[u + 1];
while (l < r){
LL k = (l + r) >> 1;
if (Cross(x * k, y * k, x2 * k, y2 * k, n, m) > 0) l = k + 1;
else r = k;
}
l --;
while (Cross(x * l, y * l, x2 * l, y2 * l, n, m) < 0) l --;
ans.push_back(l + 1);
}
return ans;
}
};