题意
有 (n) 只巨龙,第 (i) 只的初始生命值为 (a_i),并且拥有一个生命恢复值 (p_i)。你有 (m) 把剑,每把剑有一个攻击力。现在你要用这 (m) 把剑依次杀死这 (n) 只巨龙。
在攻击第 (i) 只巨龙之前,你需要选出攻击力不大于 (a_i) 且攻击力最大的一把剑。如果这样的剑不存在,则选出攻击力最小的一把剑。设这把剑的攻击力为 (v),接下来:
- 你会连续攻击巨龙 (x) 次,巨龙的生命值变为 (a_i -x imes v);
- 巨龙会不断恢复生命值,每次恢复 (p_i),直到生命值非负;
- 此时,若巨龙生命值为 (0),巨龙死去;
- 用来攻击的这把剑会消失。与此同时,如果巨龙死去,你会获得一把新的剑,攻击力可能和这一把不同。
求 (x) 的最小非负整数解,或者指出 (x) 不存在。
(1 leq n,m leq 10^5,p_i geq 1,operatorname{lcm}(p_i) leq 10^{12},a_i leq 10^{12})
题解
观察到用来攻击每个巨龙的剑的攻击力是固定的。设攻击第 (i) 只巨龙时,用到的剑攻击力为 (b_i)。考虑列出方程组
考虑对方程进行化简,将形如 (b_ix equiv a_i pmod{p_i}) 的方程化为 (b_ix + p_iy = a_i) 的标准扩欧形式。
首先判断该方程是否有解。根据裴蜀定理,该方程有解的充要条件是 (gcd(b_i,p_i) mid a_i)。使用 exgcd 求出原方程的一组特解 (x = x_0),那么 (x) 的解集为 (x = x_0 + k dfrac{p_i}{gcd(b_i,p_i)})。再化回同余方程的形式,得 (x equiv x_0 pmod{dfrac{p_i}{gcd(b_i,p_i)}})。
对于每个方程进行上述处理,便得到了可以使用 exCRT 的形式。设该方程组的最小非负整数解为 (s)。注意到 (s) 不一定是原问题的一个合法解,因为 (s) 次攻击可能不一定会使某条巨龙的生命值变为非正数。这个时候就需要将 (s) 不断增加 (operatorname{lcm}(dfrac{p_i}{gcd(b_i,p_i)}))(即新方程组模数的最小公倍数),直到 (s) 次攻击可以把所有巨龙的生命值变为非正数。
# include <bits/stdc++.h>
# define int __int128
const int N=100010,INF=0x3f3f3f3f;
int n,m;
int a[N],p[N],b[N],c[N];
std::multiset <int> S;
int pval[N],bval[N];
int maxx;
inline int read(void){
int res,f=1;
char c;
while((c=getchar())<'0'||c>'9')
if(c=='-')f=-1;
res=c-48;
while((c=getchar())>='0'&&c<='9')
res=res*10+c-48;
return res*f;
}
void print(int x){
if(x<0)
putchar('-'),x=-x;
if(x>9)
print(x/10);
putchar(x%10+'0');
return;
}
inline int exgcd(int a,int b,int &x,int &y){
if(!b){
x=1,y=0;
return a;
}
int gcd=exgcd(b,a%b,x,y),Temp;
Temp=x,x=y,y=Temp-(a/b)*y;
return gcd;
}
inline bool build_excrt(void){
for(int i=1;i<=n;++i){
int A=c[i],B=p[i],C=a[i],xz,yz;
int gcd=exgcd(A,B,xz,yz);
if(C%gcd)
return false;
bval[i]=xz*C/gcd,pval[i]=B/gcd,bval[i]%=pval[i];
}
return true;
}
inline int solve(void){
if(!build_excrt()){
return -1;
}
int M=pval[1],ans=bval[1];
for(int i=2;i<=n;++i){
int A=M,B=pval[i],C=((bval[i]-ans)%pval[i]+pval[i])%pval[i],x,y,gcd;
gcd=exgcd(A,B,x,y);
if(C%gcd)
return -1;
ans+=C/gcd*x*M;
M=M/gcd*pval[i];
ans=(ans%M+M)%M;
}
if(ans<maxx)
ans+=((maxx-ans-1)/M+1)*M;
return ans;
}
signed main(void){
int T=read();
while(T--){
maxx=0;
S.clear(),n=read(),m=read();
for(int i=1;i<=n;++i)
a[i]=read();
for(int i=1;i<=n;++i)
p[i]=read();
for(int i=1;i<=n;++i)
b[i]=read();
while(m--)
S.insert(read());
for(int i=1;i<=n;++i){
std::multiset <int>::iterator it=S.upper_bound(a[i]);
if(S.begin()!=it)
--it;
c[i]=*it,S.erase(it),S.insert(b[i]);
maxx=std::max(maxx,(a[i]-1)/c[i]+1);
}
print(solve()),puts("");
}
return 0;
}