题面好啰嗦啊直接粘LOJ题面好了php
小 D 最近在网上发现了一款小游戏。游戏的规则以下:c++
- 游戏的目标是按照编号 \(1\)~\(n\) 顺序杀掉 \(n\) 条巨龙,每条巨龙拥有一个初始的生命值 \(a_i\) 。同时每条巨龙拥有恢复能力,当其使用恢复能力时,它的生命值就会每次增长 \(p_i\),直至生命值非负。只有在攻击结束后且当生命值刚好为 \(0\) 时它才会死去。
- 游戏开始时玩家拥有 \(m\) 把攻击力已知的剑,每次面对巨龙时,玩家只能选择一把剑,当杀死巨龙后这把剑就会消失,但做为奖励,玩家会得到全新的一把剑。
小 D 以为这款游戏十分无聊,但最快通关的玩家能够得到 ION2018 的参赛资格, 因而小 D 决定写一个笨笨的机器人帮她通关这款游戏,她写的机器人遵循如下规则:测试
- 每次面对巨龙时,机器人会选择当前拥有的,攻击力不高于巨龙初始生命值中攻击力最大的一把剑做为武器。若是没有这样的剑,则选择攻击力最低的一把剑做为武器。
- 机器人面对每条巨龙,它都会使用上一步中选择的剑攻击巨龙固定的 \(x\) 次,使巨龙的生命值减小 \(x \times ATK\)。
- 以后,巨龙会不断使用恢复能力,每次恢复 \(p_i\) 生命值。若在使用恢复能力前或某一次恢复后其生命值为 \(0\),则巨龙死亡,玩家经过本关。
那么显然机器人的攻击次数是决定可否最快通关这款游戏的关键。小 D 如今得知了每条巨龙的全部属性,她想考考你,你知道应该将机器人的攻击次数 \(x\) 设置为多少,才能用最少的攻击次数通关游戏吗?ui
固然若是不管设置成多少都没法通关游戏,输出 \(-1\) 便可。spa
杀龙的时候须要三步必杀, 不能重复屡次qwq...3d
\(n\le 10^5,m\le 10^5,a_i\le 10^{12}\).rest
对于全部的测试点,\(T \le 5\),全部武器的攻击力 \(\le 10^6\),全部 \(p_i\) 的最小公倍数 \(\le 10^{12}\)。code
乍一看这题神仙的一匹, 然而冷静分析一下能够发现:blog
拿来淦龙的剑都是固定的, 并且每次至关于把龙的血打到一个不大于 \(0\) 的 \(p_i\) 的倍数就能够完成任务了.游戏
那么也就是说只要让 \(x\) 知足:
\[ x\times ATK_i\equiv a_i \pmod {p_i}\\ x\times ATK_i\ge a_i \]
先来搞第一个限制.
不难发现这个限制至关于下式:
\[ x\times ATK_i+k\times p_i=a_i \]
其中 \(ATK_i,p_i,a_i\) 都是肯定的, 显然这个东西能够随手 ExGCD 搞一搞解出一个 \(\bmod p_i\) 意义下的 \(x\).
不过注意这一步要对 \(ATK_i\) 和 \(p_i\) 进行约分, 否则逆元不惟一就会解出奇怪的东西qaq(sb rvalue在这坑了一个小时)...
而后咱们不难发现咱们获得了一堆形如这样的方程组:
\[ x\equiv r_i \pmod {p_i} \]
这不裸的 CRT 么?
然而 \(p_i\) 不互质. 并不能直接 CRT.
考虑 ExCRT. ExCRT 的大致思路是经过 ExGCD 来合并两个同余方程.
假设咱们如今有两个同余方程:
\[ \begin{cases} x\equiv a &\pmod n\\ x\equiv b &\pmod m \end{cases} \]
不难发现它等价于:
\[ \begin{cases} x = a + pn\\ x = b + qm \end{cases} \]
那么也就是说:
\[ a+pn=b+qm \]
移项可得:
\[ a-b=qm-pn \]
好了咱们能够 ExGCD 了.
算出 \(p\) 和 \(q\) 以后能够用 \(x=a+pn=b+pm\) 算出 \(x\) 来. 它与全部 \(\bmod \gcd(n,m)\) 意义下同余的值都是这两个方程的解. 显然咱们直接对 \(\gcd(n,m)\) 取模就能够获得最小值了.
一直这样合并下去, 只要 ExGCD 的时候出锅那么根据Bézout定理这个方程组无解.
而后咱们能够获得一个 \(\bmod \operatorname{lcm} \{p_i\}\) 的 \(x\). 接着考虑第二种限制.
显然咱们能够对当前已有的 \(x\) 计算它是否知足 \(x\times ATK_i\ge a_i\), 若是不知足的话把 \(x\) 变为 \(\bmod \operatorname{lcm} \{p_i\}\) 意义下同余的最小知足条件的值就能够了. 不难发现答案就是:
\[ x+\operatorname{lcm}\{p_i\}\times \max_{1\le i\le n}\left \lceil \frac {\left \lceil \frac{a_i}{ATK_i}\right \rceil-x}{\operatorname{lcm}\{p_i\}}\right \rceil \]
UOJ由于有Hack因此数据比LOJ强一些, LOJ过了以后建议在UOJ上交一发.
下面这份代码在发文时没有被Hack qwq...
#include <bits/stdc++.h> const int MAXN=1e5+10; typedef long long intEx; struct Equation{ intEx mod; intEx rest; }; Equation E[MAXN]; int n; int m; intEx a[MAXN]; intEx p[MAXN]; intEx w[MAXN]; intEx atk[MAXN]; intEx Mul(intEx,intEx,intEx); intEx ExGCD(intEx,intEx,intEx&,intEx&); int main(){ int T; scanf("%d",&T); while(T--){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%lld",a+i); for(int i=1;i<=n;i++) scanf("%lld",p+i); for(int i=1;i<=n;i++) scanf("%lld",w+i); std::multiset<intEx> s; for(int i=1;i<=m;i++){ intEx x; scanf("%lld",&x); s.insert(x); } try{ for(int i=1;i<=n;i++){ E[i].mod=p[i]; auto it=s.upper_bound(a[i]); if(it!=s.begin()) --it; intEx t; atk[i]=*it; s.erase(it); intEx gcd=ExGCD(atk[i],p[i],E[i].rest,t); if(a[i]%gcd!=0) throw std::logic_error(R"(a[i]%gcd!=0)"); E[i].mod/=gcd; E[i].rest=Mul(a[i]/gcd,E[i].rest,E[i].mod); (E[i].rest+=E[i].mod)%=E[i].mod; s.insert(w[i]); } for(int i=2;i<=n;i++){ intEx x,y; intEx gcd=ExGCD(E[i-1].mod,E[i].mod,x,y); intEx lcm=E[i-1].mod/gcd*E[i].mod; if((E[i-1].rest-E[i].rest)%gcd!=0) throw std::logic_error(R"((E[i].rest-E[i-1].rest)%gcd!=0 @)"+std::to_string(i)); intEx scale=(E[i-1].rest-E[i].rest)/gcd; (E[i].rest+=Mul(Mul(scale,y,lcm),E[i].mod,lcm))%=lcm; E[i].mod=lcm; E[i].rest=(E[i].rest+lcm)%lcm; } intEx scale=0; for(int i=1;i<=n;i++) scale=std::max(scale,((a[i]+atk[i]-1)/atk[i]-E[n].rest+E[n].mod-1)/E[n].mod); printf("%lld\n",E[n].rest+scale*E[n].mod); } catch(std::logic_error x){ puts("-1"); continue; } } return 0; } intEx ExGCD(intEx a,intEx b,intEx& x,intEx& y){ if(b==0){ x=1; y=0; return a; } intEx gcd=ExGCD(b,a%b,y,x); y-=(a/b)*x; return gcd; } intEx Mul(intEx a,intEx b,intEx p){ return __int128(a)*b%p; }