【TCO2013 3B】ToastJumping

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;
	}
};


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值