题目描述
小 D 最近在网上发现了一款小游戏。游戏的规则如下:
-
游戏的目标是按照编号 (1 ightarrow n) 顺序杀掉 (n) 条巨龙,每条巨龙拥有一个初始的生命值 (a_i) 。同时每条巨龙拥有恢复能力,当其使用恢复能力时,它的生命值就会每次增加 (p_i) ,直至生命值非负。只有在攻击结束后且当生命值恰好为 (0) 时它才会死去。
-
游戏开始时玩家拥有 (m) 把攻击力已知的剑,每次面对巨龙时,玩家只能选择一把剑,当杀死巨龙后这把剑就会消失,但作为奖励,玩家会获得全新的一把剑。
小 D 觉得这款游戏十分无聊,但最快通关的玩家可以获得 ION2018 的参赛资格,于是小 D 决定写一个笨笨的机器人帮她通关这款游戏,她写的机器人遵循以下规则:
-
每次面对巨龙时,机器人会选择当前拥有的,攻击力不高于巨龙初始生命值中攻击力最大的一把剑作为武器。如果没有这样的剑,则选择攻击力最低的一把剑作为武器。
-
机器人面对每条巨龙,它都会使用上一步中选择的剑攻击巨龙固定的 (x) 次,使巨龙的生命值减少 (x imes ATK) 。
-
之后,巨龙会不断使用恢复能力,每次恢复 (p_i) 生命值。若在使用恢复能力前或某一次恢复后其生命值为 (0) ,则巨龙死亡,玩家通过本关。
那么显然机器人的攻击次数是决定能否最快通关这款游戏的关键。小 D 现在得知了每条巨龙的所有属性,她想考考你,你知道应该将机器人的攻击次数 (x) 设置为多少,才能用最少的攻击次数通关游戏吗?
当然如果无论设置成多少都无法通关游戏,输出 (-1) 即可。
输入格式
第一行一个整数 (T),代表数据组数。
接下来 (T) 组数据,每组数据包含 (5) 行。
-
每组数据的第一行包含两个整数,(n) 和 (m),代表巨龙的数量和初始剑的数量;
-
接下来一行包含 (n) 个正整数,第 (i) 个数表示第 (i) 条巨龙的初始生命值 (a_i);
-
接下来一行包含 (n) 个正整数,第 (i) 个数表示第 (i) 条巨龙的恢复能力 (p_i);
-
接下来一行包含 (n) 个正整数,第 (i) 个数表示杀死第 (i) 条巨龙后奖励的剑的攻击力;
-
接下来一行包含 (m) 个正整数,表示初始拥有的 (m) 把剑的攻击力。
输出格式
一共 (T) 行。
第 (i) 行一个整数,表示对于第 (i) 组数据,能够使得机器人通关游戏的最小攻击次数 (x),如果答案不存在,输出 (-1) 。
输入输出样例
输入
2
3 3
3 5 7
4 6 10
7 3 9
1 9 1000
3 2
3 5 6
4 8 7
1 1 1
1 1
输出
59
-1
说明/提示
更多样例
更多样例请在附加文件中下载。
样例 2
见附加文件中的 dragon2.in
与 dragon2.ans
。
样例 1 解释
第一组数据:
-
开始时拥有的剑的攻击力为 ({1,9,10}),第 (1) 条龙生命值为 (3),故选择攻击力为 (1) 的剑,攻击 (59) 次,造成 (59) 点伤害,此时龙的生命值为 (-56),恢复 (14) 次后生命值恰好为 (0),死亡。
-
攻击力为 (1) 的剑消失,拾取一把攻击力为 (7) 的剑,此时拥有的剑的攻击力为 ({7,9,10}),第 (2) 条龙生命值为 (5),故选择攻击力为 (7) 的剑,攻击 (59) 次,造成 (413) 点伤害,此时龙的生命值为 (-408),恢复 (68) 次后生命值恰好为 (0),死亡。
-
此时拥有的剑的攻击力为 ({3,9,10}),第 (3) 条龙生命值为 (7),故选择攻击力为 (3) 的剑,攻击 (59) 次,造成 (177) 点伤害,此时龙的生命值为 (-170),恢复 (17) 次后生命值恰好为 (0),死亡。
-
没有比 (59) 次更少的通关方法,故答案为 (59) 。
第二组数据:
- 不存在既能杀死第一条龙又能杀死第二条龙的方法,故无法通关,输出 (-1) 。
子任务
测试点编号 | (n) | (m) | (p_i) | (a_i) | 攻击力 | 其他限制 |
---|---|---|---|---|---|---|
1 | (le 10^5) | (=1) | (=1) | (le 10^5) | (=1) | 无 |
2 | (le 10^5) | (=1) | (=1) | (le 10^5) | (=1) | 无 |
3 | (le 10^5) | (=1) | (=1) | (le 10^5) | (le 10^5) | 无 |
4 | (le 10^5) | (=1) | (=1) | (le 10^5) | (le 10^5) | 无 |
5 | (le 10^3) | (le 10^3) | (le 10^5) | (le 10^5) | (le 10^5) | 特性 1、特性 2 |
6 | (le 10^3) | (le 10^3) | (le 10^5) | (le 10^5) | (le 10^5) | 特性 1、特性 2 |
7 | (le 10^3) | (le 10^3) | (le 10^5) | (le 10^5) | (le 10^5) | 特性 1、特性 2 |
8 | (=1) | (=1) | (le 10^8) | (le 10^8) | (le 10^6) | 特性 1 |
9 | (=1) | (=1) | (le 10^8) | (le 10^8) | (le 10^6) | 特性 1 |
10 | (=1) | (=1) | (le 10^8) | (le 10^8) | (le 10^6) | 特性 1 |
11 | (=1) | (=1) | (le 10^8) | (le 10^8) | (le 10^6) | 特性 1 |
12 | (=1) | (=1) | (le 10^8) | (le 10^8) | (le 10^6) | 特性 1 |
13 | (=1) | (=1) | (le 10^8) | (le 10^8) | (le 10^6) | 特性 1 |
14 | (=10^5) | (=10^5) | (=1) | (le 10^8) | (le 10^6) | 无特殊限制 |
15 | (=10^5) | (=10^5) | (=1) | (le 10^8) | (le 10^6) | 无特殊限制 |
16 | (le 10^5) | (le 10^5) | 所有 (p_i) 是质数 | (le 10^{12}) | (le 10^6) | 特性 1 |
17 | (le 10^5) | (le 10^5) | 所有 (p_i)是质数 | (le 10^{12}) | (le 10^6) | 特性 1 |
18 | (le 10^5) | (le 10^5) | 无特殊限制 | (le 10^{12}) | (le 10^6) | 特性 1 |
19 | (le 10^5) | (le 10^5) | 无特殊限制 | (le 10^{12}) | (le 10^6) | 特性 1 |
20 | (le 10^5) | (le 10^5) | 无特殊限制 | (le 10^{12}) | (le 10^6) | 特性 1 |
特性 1 是指:对于任意的 (i),(a_i le p_i) 。
特性 2 是指:(operatorname{lcm}(p_i) le 10^6),即所有 (p_i) 的最小公倍数不大于 (10^6) 。
对于所有的测试点,(T le 5),所有武器的攻击力 (le 10^6),所有 (p_i) 的最小公倍数 (le 10^{12}) 。
保证 (T, n, m) 均为正整数。
提示
你所用到的中间结果可能很大,注意保存中间结果的变量类型。
附件下载
题解
首先很容易将题意转化为
给定若干个如下式的不定方程,解出最小解 (x)
[ATK_i imes xequiv a_i mod p_i ]
对于单个不定方程,我们可以化简一下
为什么化简?
如果 (gcd (ATK_i,p_i) eq 1),会发现,可以解出 (gcd (ATK_i,p_i)) 个解使得这个不定方程成立
但是我们最后要求解的是一个不定方程组,无法得知取那个解是正确的,所以两边同除 (gcd (ATK_i,p_i)),使得不定方程解出一个解,从而利于不定方程组的求解
然后用 (ExGCD) 来求解,化成若干个同余方程的形式,最后 (ExCRT) 合并一下就好了
但是上述做法并不能处理 (p_i=1) 的情况,所以要特判一下,其实就是 (max {frac{a_i}{ATK_i}}),注意上取整
作为 (NOI; 2018) 的签到题,还是挺水的吧
代码
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
#include <cmath>
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
const int maxn = 1e5 + 50, INF = 0x3f3f3f3f;
inline ll read () {
register ll x = 0, w = 1;
register char ch = getchar ();
for (; ch < '0' || ch > '9'; ch = getchar ()) if (ch == '-') w = -1;
for (; ch >= '0' && ch <= '9'; ch = getchar ()) x = x * 10 + ch - '0';
return x * w;
}
inline void write (register int x) {
if (x / 10) write (x / 10);
putchar (x % 10 + '0');
}
int T, n, m;
bool ALLONE = 1;
vector <int> vec;
ll maxx, a[maxn], b[maxn], c[maxn], p[maxn], atk[maxn];
inline ll Mul (register ll a, register ll b, register ll mod) {
register ll tmp = (long double) a / mod * b, res = (ull) a * b - (ull) tmp * mod;
return (res % mod + mod) % mod;
}
inline ll ExGCD (register ll a, register ll b, register ll &x, register ll &y) {
if (! b) return x = 1, y = 0, a;
register ll d = ExGCD (b, a % b, x, y), tmp = x;
return x = y, y = tmp - a / b * y, d;
}
inline ll ExCRT () {
register ll ans = c[1], lcm = p[1], maxx = 0;
for (register int i = 2; i <= n; i ++) {
register ll res = ((c[i] - ans) % p[i] + p[i]) % p[i], x = 0, y = 0, gcd = ExGCD (lcm, p[i], x, y), tmp = p[i] / gcd;
if (res % gcd) return -1;
x = Mul (x, res / gcd, tmp), ans += x * lcm, lcm *= tmp, ans = (ans % lcm + lcm) % lcm, maxx = max (maxx, p[i]);
}
return (ans % lcm + lcm) % lcm;
}
int main () {
T = read();
while (T --) {
n = read(), m = read(), vec.clear (), ALLONE = 1, maxx = 0;
for (register int i = 1; i <= n; i ++) a[i] = read();
for (register int i = 1; i <= n; i ++) {
p[i] = read();
if (p[i] != 1) ALLONE = 0;
}
for (register int i = 1; i <= n; i ++) atk[i] = read();
for (register int i = 1; i <= m; i ++) vec.push_back (read ());
sort (vec.begin (), vec.end ());
for (register int i = 1, res; i <= n; i ++) {
res = upper_bound (vec.begin (), vec.end (), a[i]) - vec.begin ();
if (res) res --;
b[i] = vec[res], vec.erase (vec.begin () + res);
res = lower_bound (vec.begin (), vec.end (), atk[i]) - vec.begin ();
vec.insert (vec.begin () + res, atk[i]);
}
if (ALLONE) {
for (register int i = 1; i <= n; i ++) maxx = max (maxx, (ll) ceil (1.0 * a[i] / b[i]));
printf ("%lld
", maxx);
} else {
for (register int i = 1; i <= n; i ++) {
register ll x = 0, y = 0, res = ExGCD (b[i], p[i], x, y);
x = 0, y = 0, res = ExGCD (res, a[i], x, y), b[i] /= res, p[i] /= res, a[i] /= res, x = 0, y = 0;
register ll gcd = ExGCD (b[i], p[i], x, y), tmp = p[i] / gcd;
if (a[i] % gcd) { puts ("-1"); goto end; }
else x = (x % tmp + tmp) % tmp, c[i] = Mul (x, a[i] / gcd, tmp);
}
printf ("%lld
", ExCRT ()); end:;
}
}
return 0;
}