洛谷传送门
BZOJ传送门
题目描述
小D 最近在网上发现了一款小游戏。游戏的规则如下:
- 游戏的目标是按照编号 1→n 1 → n 顺序杀掉 n n 条巨龙,每条巨龙拥有一个初始的生命值 。同时每条巨龙拥有恢复能力,当其使用恢复能力时,它的生命值就会每次增加 pi p i ,直至生命值非负。只有在攻击结束后且当生命值恰好为 0 0 时它才会死去。
- 游戏开始时玩家拥有 把攻击力已知的剑,每次面对巨龙时,玩家只能选择一 把剑,当杀死巨龙后这把剑就会消失,但作为奖励,玩家会获得全新的一把剑。 小D 觉得这款游戏十分无聊,但最快通关的玩家可以获得ION2018 的参赛资格, 于是小D 决定写一个笨笨的机器人帮她通关这款游戏,她写的机器人遵循以下规则:
- 每次面对巨龙时,机器人会选择当前拥有的,攻击力不高于巨龙初始生命值中攻击力最大的一把剑作为武器。如果没有这样的剑,则选择攻击力最低的一把剑作为武器。
- 机器人面对每条巨龙,它都会使用上一步中选择的剑攻击巨龙固定的 x x 次,使巨龙的生命值减少 。
- 之后,巨龙会不断使用恢复能力,每次恢复 pi p i 生命值。若在使用恢复能力前或某一次恢复后其生命值为 0 0 ,则巨龙死亡,玩家通过本关。
那么显然机器人的攻击次数是决定能否最快通关这款游戏的关键。小 D 现在得知了每条巨龙的所有属性,她想考考你,你知道应该将机器人的攻击次数 设置为多少,才能用最少的攻击次数通关游戏吗?
当然如果无论设置成多少都无法通关游戏,输出 −1 − 1 即可。
输入输出格式
输入格式:
从文件dragon.in 中读入数据。
第一行一个整数 T T ,代表数据组数。
接下来 组数据,每组数据包含 5 5 行。
- 每组数据的第一行包含两个整数, 和 m m ,代表巨龙的数量和初始剑的数量;
- 接下来一行包含 个正整数,第 i i 个数表示第 条巨龙的初始生命值 ai a i ;
- 接下来一行包含 n n 个正整数,第 个数表示第 i i 条巨龙的恢复能力 ;
- 接下来一行包含 n n 个正整数,第 个数表示杀死第 i i 条巨龙后奖励的剑的攻击力;
- 接下来一行包含 个正整数,表示初始拥有的 m m 把剑的攻击力。
输出格式:
输出到文件dragon.out 中。 一共 行。
第 i i 行一个整数,表示对于第 组数据,能够使得机器人通关游戏的最小攻击次数 x 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
输出样例#1:
59
-1
说明
第一组数据:
- 开始时拥有的剑的攻击力为 {1,9,10} { 1 , 9 , 10 } ,第 1 1 条龙生命值为,故选择攻击力为 1 1 的剑,攻击 次,造成 59 59 点伤害,此时龙的生命值为 −56 − 56 ,恢复 14 14 次后生命值恰好为 0 0 ,死亡。
- 攻击力为 的剑消失,拾取一把攻击力为 7 7 的剑,此时拥有的剑的攻击力为 ,第 2 2 条龙生命值为,故选择攻击力为 7 7 的剑,攻击 次,造成 413 413 点伤害,此时龙的生命值为 −408 − 408 ,恢复 68 68 次后生命值恰好为 0 0 ,死亡。
- 此时拥有的剑的攻击力为,第 3 3 条龙生命值为,故选择攻击力为 3 3 的剑,攻击 次,造成 177 177 点伤害,此时龙的生命值为 −170 − 170 ,恢复 17 17 次后生命值恰好为 0 0 ,死亡。
- 没有比 次更少的通关方法,故答案为 59 59 。
第二组数据: 不存在既能杀死第一条龙又能杀死第二条龙的方法,故无法通关,输出 −1 − 1 。
【子任务】
测试点编号 | n n | pi p i | ai a i | 攻击力 | 其他限制 | |
---|---|---|---|---|---|---|
1 | ≤105 ≤ 10 5 | =1 = 1 | =1 = 1 | ≤105 ≤ 10 5 | =1 = 1 | 无 |
2 | ≤105 ≤ 10 5 | =1 = 1 | =1 = 1 | ≤105 ≤ 10 5 | =1 = 1 | 无 |
3 | ≤105 ≤ 10 5 | =1 = 1 | =1 = 1 | ≤105 ≤ 10 5 | ≤105 ≤ 10 5 | 无 |
4 | ≤105 ≤ 10 5 | =1 = 1 | =1 = 1 | ≤105 ≤ 10 5 | ≤105 ≤ 10 5 | 无 |
5 | ≤103 ≤ 10 3 | ≤103 ≤ 10 3 | ≤105 ≤ 10 5 | ≤105 ≤ 10 5 | ≤105 ≤ 10 5 | 特性 1、特性 2 |
6 | ≤103 ≤ 10 3 | ≤103 ≤ 10 3 | ≤105 ≤ 10 5 | ≤105 ≤ 10 5 | ≤105 ≤ 10 5 | 特性 1、特性 2 |
7 | ≤103 ≤ 10 3 | ≤103 ≤ 10 3 | ≤105 ≤ 10 5 | ≤105 ≤ 10 5 | ≤105 ≤ 10 5 | 特性 1、特性 2 |
8 | =1 = 1 | =1 = 1 | ≤108 ≤ 10 8 | ≤108 ≤ 10 8 | ≤106 ≤ 10 6 | 特性 1 |
9 | =1 = 1 | =1 = 1 | ≤108 ≤ 10 8 | ≤108 ≤ 10 8 | ≤106 ≤ 10 6 | 特性 1 |
10 | =1 = 1 | =1 = 1 | ≤108 ≤ 10 8 | ≤108 ≤ 10 8 | ≤106 ≤ 10 6 | 特性 1 |
11 | =1 = 1 | =1 = 1 | ≤108 ≤ 10 8 | ≤108 ≤ 10 8 | ≤106 ≤ 10 6 | 特性 1 |
12 | =1 = 1 | =1 = 1 | ≤108 ≤ 10 8 | ≤108 ≤ 10 8 | ≤106 ≤ 10 6 | 特性 1 |
13 | =1 = 1 | =1 = 1 | ≤108 ≤ 10 8 | ≤108 ≤ 10 8 | ≤106 ≤ 10 6 | 特性 1 |
14 | =105 = 10 5 | =105 = 10 5 | =1 = 1 | ≤108 ≤ 10 8 | ≤106 ≤ 10 6 | 无特殊限制 |
15 | =105 = 10 5 | =105 = 10 5 | =1 = 1 | ≤108 ≤ 10 8 | ≤106 ≤ 10 6 | 无特殊限制 |
16 | ≤105 ≤ 10 5 | ≤105 ≤ 10 5 | 所有 pi p i 是质数 | ≤1012 ≤ 10 12 | ≤106 ≤ 10 6 | 特性 1 |
17 | ≤105 ≤ 10 5 | ≤105 ≤ 10 5 | 所有 pi p i 是质数 | ≤1012 ≤ 10 12 | ≤106 ≤ 10 6 | 特性 1 |
18 | ≤105 ≤ 10 5 | ≤105 ≤ 10 5 | 无特殊限制 | ≤1012 ≤ 10 12 | ≤106 ≤ 10 6 | 特性 1 |
19 | ≤105 ≤ 10 5 | ≤105 ≤ 10 5 | 无特殊限制 | ≤1012 ≤ 10 12 | ≤106 ≤ 10 6 | 特性 1 |
20 | ≤105 ≤ 10 5 | ≤105 ≤ 10 5 | 无特殊限制 | ≤1012 ≤ 10 12 | ≤106 ≤ 10 6 | 特性 1 |
特性 1 是指:对于任意的 i i ,。
特性 2 是指: lcm(pi)≤106 lcm ( p i ) ≤ 10 6 ,即所有 pi p i 的最小公倍数不大于 106 10 6 。
对于所有的测试点, T≤5 T ≤ 5 ,所有武器的攻击力 ≤106 ≤ 10 6 ,所有 pi p i 的最小公倍数 ≤1012 ≤ 10 12 。
保证 T,n,m T , n , m 均为正整数。
【提示】
你所用到的中间结果可能很大,注意保存中间结果的变量类型。
解题分析
一道 EXCRT E X C R T 板题, 我在同步赛上连暴力分都没拿满, 真的是菜啊……
这道题无非是求这玩意:
的一组最小整数解。
ATKi A T K i 显然是确定的, 我们用一个 multiset m u l t i s e t 维护一下就好了, 注意 multiset m u l t i s e t 的 erase e r a s e 如果是 erase e r a s e 掉一个值的话是删除所有相同的值…
上面这个玩意看起来像
EXCRT
E
X
C
R
T
, 但是
EXCRT
E
X
C
R
T
好像左边没有系数? 那我们来化简一下。
这玩意我们可以用
EXGCD
E
X
G
C
D
解出一组最小整数解, 大概是这样的:
注意判断是否有解。
那么之后我们就可以欢快地用
EXCRT
E
X
C
R
T
化简了。式子在上一步处理之后都变成了这样:
那么我们对于其中两个式子作如下操作:
可以解出这个玩意(同时判是否有解), 然后代回得到:
这是因为上面的 piyi−pi+1yi+1 p i y i − p i + 1 y i + 1 必须同时加上 lcm(pi,pi+1) l c m ( p i , p i + 1 ) 才能从新取到 ci+1−ci c i + 1 − c i 。
大概就这样了, 至于中间结果保存的变量类型? €€£又不准用__int128,只好写龟速乘。
最后检查一下 k k <script type="math/tex" id="MathJax-Element-414">k</script>是否满足要求, 因为可能模得太小, 需要记录一个至少砍多少刀的值。
代码如下:
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cctype>
#include <cmath>
#include <algorithm>
#include <set>
#define R register
#define IN inline
#define gc getchar()
#define W while
#define MX 100050
#define ll long long
template <class T>
IN void in(T &x)
{
x = 0; R char c = gc;
for (; !isdigit(c); c = gc);
for (; isdigit(c); c = gc)
x = (x << 1) + (x << 3) + c - 48;
}
std::multiset <ll> st;
std::multiset <ll> :: iterator it;
int dra, swd, T;
ll lif[MX], ref[MX], rew[MX], md, tim, mx, atk;
ll exgcd(ll a, ll b, ll &x, ll &y)
{
if(!b) return x = 1, y = 0, a;
ll ret= exgcd(b, a % b, x, y);
ll buf = x; x = y, y = buf - a / b * y;
return ret;
}
IN ll fmul(ll a, ll b, ll mod)
{
a = (a % mod + mod) % mod, b = (b % mod + mod) % mod;
ll ans = 0;
W (b)
{
if(b & 1) ans = (ans + a) % mod;
a = (a << 1) % mod; b >>= 1;
}
return ans;
}
int main(void)
{
in(T); ll buf, x, y, gcd, N, old, a, b;
start: W (T--)
{
st.clear();
in(dra), in(swd);
for (R int i = 1; i <= dra; ++i) in(lif[i]);
for (R int i = 1; i <= dra; ++i) in(ref[i]);
for (R int i = 1; i <= dra; ++i) in(rew[i]);
for (R int i = 1; i <= swd; ++i) in(buf), st.insert(buf);
mx = tim = 0, md = 1;
for (R int i = 1; i <= dra; ++i)
{
it = st.upper_bound(lif[i]);
if(it != st.begin()) it--;
atk = *it; st.erase(it), st.insert(rew[i]);
mx = std::max(mx, (lif[i] - 1) / atk + 1);
atk %= ref[i], lif[i] %= ref[i];
if(!atk && lif[i]) {puts("-1"); goto start;}
if(!atk && !lif[i]) continue;
gcd = exgcd(atk, ref[i], x, y);//化简
if(lif[i] % gcd) {puts("-1"); goto start;}
N = lif[i] / gcd, ref[i] /= gcd;
lif[i] = fmul((x % ref[i] + ref[i]) % ref[i], N, ref[i]);
gcd = exgcd(md, ref[i], x, y);//合并
if((tim - lif[i]) % gcd) {puts("-1"); goto start;}
a = md, b = ref[i]; b /= gcd;
x = fmul(x, ((lif[i] - tim) / gcd % b + b) % b, b);
old = md; md = md / gcd * ref[i];
tim = (tim + fmul(x, old, md) % md + md) % md;
}
printf("%lld\n", tim >= mx ? tim : tim + md * ((mx - tim - 1) / md + 1));//注意是否过小
}
}