题目描述
小D 最近在网上发现了一款小游戏。游戏的规则如下:
游戏的目标是按照编号 顺序杀掉 条巨龙,每条巨龙拥有一个初始的生命值 。同时每条巨龙拥有恢复能力,当其使用恢复能力时,它的生命值就会每次增加 ,直至生命值非负。只有在攻击结束后且当生命值恰好为 时它才会死去。
游戏开始时玩家拥有 把攻击力已知的剑,每次面对巨龙时,玩家只能选择一 把剑,当杀死巨龙后这把剑就会消失,但作为奖励,玩家会获得全新的一把剑。 小D 觉得这款游戏十分无聊,但最快通关的玩家可以获得ION2018 的参赛资格, 于是小D 决定写一个笨笨的机器人帮她通关这款游戏,她写的机器人遵循以下规则:
每次面对巨龙时,机器人会选择当前拥有的,攻击力不高于巨龙初始生命值中攻击力最大的一把剑作为武器。如果没有这样的剑,则选择攻击力最低的一把剑作为武器。
机器人面对每条巨龙,它都会使用上一步中选择的剑攻击巨龙固定的 次,使巨龙的生命值减少 。
之后,巨龙会不断使用恢复能力,每次恢复 生命值。若在使用恢复能力前或某一次恢复后其生命值为 ,则巨龙死亡,玩家通过本关。
那么显然机器人的攻击次数是决定能否最快通关这款游戏的关键。小 D 现在得知了每条巨龙的所有属性,她想考考你,你知道应该将机器人的攻击次数 设置为多少,才能用最少的攻击次数通关游戏吗?
当然如果无论设置成多少都无法通关游戏,输出 即可。
输入格式:
从文件dragon.in 中读入数据。
第一行一个整数T ,代表数据组数。
接下来T 组数据,每组数据包含5行。
每组数据的第一行包含两个整数,
和
,代表巨龙的数量和初始剑的数量;
接下来一行包含
个正整数,第
个数表示第
条巨龙的初始生命值
接下来一行包含
个正整数,第
个数表示第
条巨龙的恢复能力
接下来一行包含
个正整数,第
个数表示杀死第
条巨龙后奖励的剑的攻击力;
接下来一行包含
个正整数,表示初始拥有的
把剑的攻击力。
输出格式:
输出到文件dragon.out 中。 一共 行。
第 行一个整数,表示对于第 组数据,能够使得机器人通关游戏的最小攻击次数 ,如果答案不存在,输出
说明
特性 1 是指:对于任意的
,
特性 2 是指:
即所有
的最小公倍数不大于
对于所有的测试点,
,所有武器的攻击力
,所有
的最小公倍数
保证
均为正整数。
【提示】
你所用到的中间结果可能很大,注意保存中间结果的变量类型。
题目分析
由题意可得式子
并化为
的形式
得
记
上式可由扩展欧几里得最小非负整数解
以及通解
题目所求答案
与上述
关系为
化为同余方程
的形式
得
也就是说每条巨龙都可以由上述转化得到一个同余方程
最后组成一个同余方程组
直接扩展中国剩余定理 EXCRT求解
不过要特别注意
EXCRT所求出的最小非负整数解并不是我们要的答案
由于每条龙都有固定的剑
所以对于每条龙至少要砍几下是已知的
我们要记录下每条龙至少要砍的次数中的最大值mi
然后EXCRT求出的答案应该是
通解中大于等于mi且最小的解
另外由于本蒟蒻实在太蒟
根本不会什么multiset
于是手写了一个treap,代码略长=_=
还有还有,一定要用龟速乘!!!
#include<iostream>
#include<vector>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cstdio>
using namespace std;
typedef long long lt;
lt read()
{
lt f=1,x=0;
char ss=getchar();
while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
return f*x;
}
const lt inf=1e12;
const int maxn=500010;
int t,n,m;
lt ai[maxn],pi[maxn],at[maxn],atk[maxn];
lt a[maxn],b[maxn],mi;
struct node
{
node* ch[2];
lt v,r,sum,cnt;
node(lt v) :v(v) {r=rand();sum=cnt=1;ch[0]=ch[1]=NULL;}
int cmp(lt x){if(x==v)return -1;return x<v?0:1;}
void update()
{
sum=cnt;
if(ch[0])sum+=ch[0]->sum;
if(ch[1])sum+=ch[1]->sum;
}
};
node* rt;
void rotate(node* &p,int d)
{
node* k=p->ch[d^1];
p->ch[d^1]=k->ch[d];
k->ch[d]=p;
p->update(); k->update();
p=k;
}
void ins(node* &p,lt x)
{
if(p==NULL){ p=new node(x); return;}
if(x==p->v){ ++p->sum; ++p->cnt; return;}
int d=p->cmp(x);
ins(p->ch[d],x);
if(p->ch[d]->r < p->r)rotate(p,d^1);
p->update();
}
void del(node* &p,lt x)
{
if(p==NULL) return;
if(x==p->v)
{
if(p->cnt>1){ p->sum--; p->cnt--; return;}
if(p->ch[0]==NULL){node *k=p; p=p->ch[1]; delete(k); }
else if(p->ch[1]==NULL){node *k=p; p=p->ch[0]; delete(k); }
else
{
int dd=p->ch[0]->r < p->ch[1]->r ?1 :0;
rotate(p,dd); del(p->ch[dd],x);
}
}
else if(x < p->v)del(p->ch[0],x);
else del(p->ch[1],x);
if(p!=NULL)p->update();
}
lt pre(node* p,lt x)
{
if(p==NULL)return -inf;
if(x==p->v) return p->v;
else if(x<p->v) return pre(p->ch[0],x);
else return max(p->v,pre(p->ch[1],x));
}
lt qmin(node* p)
{
if(p->ch[0]) return qmin(p->ch[0]);
else return p->v;
}
lt mul(lt a,lt b,lt mod)
{
lt res=0;
while(b>0)
{
if(b&1) res=(res+a)%mod;
a=(a+a)%mod;
b>>=1;
}
return res;
}
lt exgcd(lt aa,lt bb,lt &x,lt &y)
{
if(bb==0){x=1;y=0;return aa;}
lt gcd=exgcd(bb,aa%bb,x,y);
lt tp=x;
x=y; y=tp-aa/bb*y;
return gcd;
}
lt excrt()
{
lt M=b[1],ans=a[1],x,y;
for(int i=2;i<=n;i++)
{
lt aa=M,bb=b[i],c=(a[i]-ans%bb+bb)%bb;
lt gcd=exgcd(aa,bb,x,y),bg=bb/gcd;
if(c%gcd!=0) return -1;
x=mul(x,c/gcd,bg);
ans+=x*M;
M*=bg;
ans=(ans%M+M)%M;
}
if(ans>=mi) return ans;//注意答案不一定是最小非负整数解
else return ans+((mi-ans)/M+((mi-ans)%M ? 1 : 0))*M;
}
int main()
{
t=read();
while(t--)
{
rt=NULL; mi=0;
n=read();m=read(); int judge=0;
for(int i=1;i<=n;++i)ai[i]=read();
for(int i=1;i<=n;++i)pi[i]=read();
for(int i=1;i<=n;++i)at[i]=read();
for(int i=1;i<=m;++i)
{
lt x=read();
ins(rt,x);
}
for(int i=1;i<=n;++i)//查找对应每条龙的剑
{
atk[i]=pre(rt,ai[i]);
if(atk[i]==-inf) atk[i]=qmin(rt);
del(rt,atk[i]); ins(rt,at[i]);
mi=max(mi,ai[i]/atk[i]+(ai[i]%atk[i]?1:0));
//记录每头龙最少要砍的次数的最大值
}
for(int i=1;i<=n;++i)//扩欧求ATK*x≡ai(mod pi)
{
lt x,y,c=ai[i];
lt gcd=exgcd(atk[i],pi[i],x,y),bg=pi[i]/gcd;
if(c%gcd!=0){ judge=1; break;}//无解
x=(mul(x,c/gcd,bg)+bg)%bg;
a[i]=x; b[i]=bg;
}
if(judge)printf("-1\n");
else printf("%lld\n",excrt());
}
return 0;
}