初始位置在 (0,0) 点,现在有两种操作:
- 操作 1 :你获得一种能力向量 (x,y) ,你可以从当前位置 (a,b) 出发,沿着这个能力向量方向移动任意整数个单位,即可以从 (a,b) 移动至 (a+dx,b+dy) ;
- 操作 2 :在二维平面上的某一点 (x,y) 上出现了一个价值为 w 的物品,如果你可以通过你当前拥有的能力向量移动至 (x,y) 就可以获得该物品。
问最终你可以获得的最大价值是多少?
简单分析即可发现,对于一个出现地物品,如果当前我能够取取得,就一定回去取,因为这对之后的操作没有影响,取得物品后可以按原路返回。
这样,问题就转换成了你有一些能力 ( x i , y i ) (x_i, y_i) (xi,yi),问它们的线性组合能否构成一个新的二元组 ( x , y ) (x,y) (x,y),对于一维情况,可以证明,所有能到达的点都是他们gcd的倍数,对于二维情况,是否也有类似的结论?之后部分截取自出题人的题解
可以证明任意时刻,能到达的点集可使用两个基向量表示。把基向量表示为标准形式:其中一个向量 ( d , 0 ) ( d ≥ 0 ) (d,0)(d \geq 0) (d,0)(d≥0),另一个向量 ( x , y ) ( x < d , y ≥ 0 ) (x,y)(x < d, y \geq 0) (x,y)(x<d,y≥0)。这样加入向量之后能快速合并。
设加入的向量为 ( a , b ) (a,b) (a,b),我们可以用 ( a , b ) (a, b) (a,b)和 ( x , y ) (x,y) (x,y)线性组合出 ( d ′ , 0 ) (d',0) (d′,0),那么新的 d d d就是 g c d ( d , d ′ ) gcd(d, d') gcd(d,d′)了。同理新的y为 g c d ( b , y ) gcd(b,y) gcd(b,y)(向量 ( d , 0 ) (d,0) (d,0)不会在这个方向产生贡献),使用扩展欧几里得求出系数,算出新的 x x x。
特别需要注意 x , y , d x,y,d x,y,d为0的情况
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll exgcd(ll a, ll b, ll &x, ll &y)
{
if(b == 0){
x = 1, y = 0;
return a;
}
ll g = exgcd(b, a % b, y, x);
y = y - a / b * x;
return g;
}
ll gcd(ll a, ll b)
{
return b ? gcd(b, a % b) : a;
}
int T, t, n;
ll dx, bx, by;
ll x, y, w, a, b, ans;
int main()
{
freopen("in.txt", "r", stdin);
scanf("%d", &T);
for(int cas = 1; cas <= T; cas++){
scanf("%d", &n);
ans = 0;
dx = bx = by = 0;
for(int i = 1; i <= n; i++){
scanf("%d", &t);
scanf("%lld%lld", &x, &y);
if(t == 1){
ll g = exgcd(y, by, a, b);
dx = gcd(dx, abs(y * bx - x * by) / g);
by = g;
bx = a * x + b * bx;
if(dx)bx = (bx % dx + dx) % dx;//两个向量基的更新
}else{
scanf("%lld", &w);
if(by && y % by == 0){
ll r = x - y / by * bx;
if(dx && r % dx == 0 || dx == r)ans += w;
}else if (by == 0){
if(bx && x % bx == 0 || bx == x)ans += w;
}
}
}
printf("Case #%d: %lld\n", cas, ans);
}
return 0;
}
这篇博客探讨了一个二维平面上的数学问题,其中涉及两种操作:沿着能力向量移动和获取价值物品。问题转化为寻找一组能力向量的线性组合能否覆盖所有物品位置。博主证明了任何时候能到达的点可以用两个基向量表示,并给出了利用扩展欧几里得算法进行动态合并的策略。通过这种方法,可以计算出最大可获取的价值。

1227

被折叠的 条评论
为什么被折叠?



