这是拓展crt的典型应用
在你开始做之前,我一定要告诉你一件事情:虽然这道题看着和拓展crt模板很像,但他俩是有巨大的区别的!不要直接把板子改吧改吧扔上去!
题目模型:求解模线性方程组
其中p1,p2...pn不一定互质
第一眼:拓展crt板子题!
第二眼:等等...好像不太对
第三眼:WTF!系数哪来的!
我们知道,拓展crt的模板只能解决x系数为1的情况,而系数不为1的是很难做的!
什么?直接乘逆元变成1?
逆元不存在呢?
我们稍微做一点推导:
首先,我们解一下方程
设这个方程的一个解是x0(这是可以使用拓展gcd求解的)
那么这个方程的通解应该是,k∈Z
那么这个通解等价于方程的解
发现什么了吗?
是的!我们证明了方程与方程
等价,这样就消掉了前面那个方程的系数!
所以,原方程组等价于这样:
这就很好了,我们使用正常的拓展crt解之即可
最后有几个细节问题:
①:对于ai>pi的情况,题目中给出的约束条件是pi=1,这样虽然拓展crt处理不了,但是我们可以应用特判过掉(p=1啊,多显然)
②:对于所有ai=pi的情况(即任一ai都=pi),只有当对应的攻击力是pi的倍数的时候才有解,否则无解,这个也要特判(有解也要特判,否则拓展crt解的结果会是0)
③:对于部分ai=pi的情况,如果对应攻击力不是pi的倍数则无解,但如果是pi的倍数,那么这个方程基本没用,可以替换成之类的形式
④:题目中运算很大,对于带取模的乘法需要快速加优化!同时所有数据类型建议使用long long以免挂掉
(求前驱那里本人使用的是treap,表示很好用)
#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#define ll long long
#define ls tree[rt].lson
#define rs tree[rt].rson
#define INF 0x3f3f3f3f
using namespace std;
ll n,m;
ll a[100005];
ll b[100005];
ll life[100005];
ll acc[100005];
ll p[100005];
ll s[100005];
int cyt=0;
int rot=0;
struct Treap
{
int lson;
int rson;
int huge;
int same;
ll val;
int rank;
}tree[200050];
void update(int rt)
{
tree[rt].huge=tree[ls].huge+tree[rs].huge+tree[rt].same;
}
void lturn(int &rt)
{
int temp=rs;
rs=tree[rs].lson;
tree[temp].lson=rt;
tree[rt].huge=tree[temp].huge;
update(temp);
rt=temp;
}
void rturn(int &rt)
{
int temp=ls;
ls=tree[ls].rson;
tree[temp].rson=rt;
tree[rt].huge=tree[temp].huge;
update(temp);
rt=temp;
}
void ins(int &rt,ll v)
{
if(!rt)
{
rt=++cyt;
tree[rt].huge=1;
tree[rt].same=1;
tree[rt].val=v;
tree[rt].rank=rand();
return;
}
if(v==tree[rt].val)
{
tree[rt].huge++;
tree[rt].same++;
return;
}else if(tree[rt].val>v)
{
ins(ls,v);
if(tree[ls].rank<tree[rt].rank)
{
rturn(rt);
}
}else
{
ins(rs,v);
if(tree[rs].rank<tree[rt].rank)
{
lturn(rt);
}
}
}
void del(int &rt,ll v)
{
if(!rt)
{
return;
}
if(tree[rt].val==v)
{
if(tree[rt].same>1)
{
tree[rt].huge--;
tree[rt].same--;
return;
}else if(ls*rs==0)
{
rt=ls+rs;
return;
}else
{
if(tree[ls].rank<tree[rs].rank)
{
rturn(rt);
del(rt,v);
}else
{
lturn(rt);
del(rt,v);
}
}
}
tree[rt].huge--;
if(tree[rt].val>v)
{
del(ls,v);
}else
{
del(rs,v);
}
update(rt);
}
void query_pro(int rt,ll v,int typ)
{
if(!rt)
{
return;
}
if(tree[rt].val==v)
{
acc[typ]=v;
return;
}else if(tree[rt].val<v)
{
acc[typ]=tree[rt].val;
query_pro(rs,v,typ);
}else
{
query_pro(ls,v,typ);
}
}
int query_min(int rt)
{
if(ls&&tree[ls].val!=-INF)
{
return query_min(ls);
}else if(tree[rt].val!=-INF)
{
return tree[rt].val;
}else
{
return query_min(rs);
}
}
ll pow_add(ll x,ll y,ll mod)
{
ll ans=0;
while(y)
{
if(y%2)
{
ans+=x;
ans%=mod;
}
y/=2;
x+=x;
x%=mod;
}
return ans;
}
ll gcd(ll x,ll y)
{
if(y==0)
{
return x;
}
return gcd(y,x%y);
}
void ex_gcd(ll a,ll b,ll &x,ll &y)
{
if(b==0)
{
x=1;
y=0;
return;
}
ex_gcd(b,a%b,x,y);
ll t=x;
x=y;
y=t-(a/b)*x;
}
bool makeit()
{
for(int i=1;i<=n;i++)
{
if(acc[i]%p[i]==0)
{
if(p[i]==life[i])
{
a[i]=1;
b[i]=0;
continue;
}else
{
printf("-1\n");
return 1;
}
}
ll x,y;
ll r=gcd(acc[i],p[i]);
if(life[i]%r)
{
printf("-1\n");
exit(0);
}
acc[i]/=r;
ll temp=life[i]/r;
ll tt=p[i]/r;
ex_gcd(acc[i],tt,x,y);
x=(pow_add(x,temp,tt)+tt)%tt;
b[i]=x;
a[i]=tt;
}
return 0;
}
ll ex_crt()
{
ll M0=a[1];
ll ans=b[1];
for(int i=2;i<=n;i++)
{
ll r=gcd(M0,a[i]);
ll bb=((b[i]-ans)%a[i]+a[i])%a[i];
if(bb%r)
{
return -1;
}
bb/=r;
ll M=M0/r;
ll aa=a[i]/r;
ll x,y;
ex_gcd(M,aa,x,y);
x=pow_add(x,bb,aa);
ans+=x*M0;
M0*=aa;
ans=(ans%M0+M0)%M0;
}
return (ans%M0+M0)%M0;
}
ll T;
int main()
{
scanf("%lld",&T);
while(T--)
{
memset(tree,0,sizeof(tree));
rot=0;
cyt=0;
ins(rot,-INF);
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%lld",&life[i]);//龙的生命力
}
bool flag=0,flag1=0;
for(int i=1;i<=n;i++)
{
scanf("%lld",&p[i]);//龙的恢复力
if(life[i]>p[i])
{
flag=1;
}
if(life[i]!=p[i])
{
flag1=1;
}
}
for(int i=1;i<=n;i++)
{
scanf("%lld",&s[i]);
}
for(int i=1;i<=m;i++)
{
ll x;
scanf("%lld",&x);
ins(rot,x);
}
for(int i=1;i<=n;i++)
{
acc[i]=0;
query_pro(rot,life[i],i);
if(acc[i]==-INF)
{
acc[i]=query_min(rot);
}
del(rot,acc[i]);
ins(rot,s[i]);
}
if(!flag1)
{
bool flag2=0;
ll ans=1;
for(int i=1;i<=n;i++)
{
if(life[i]%acc[i]!=0)
{
printf("-1\n");
flag2=1;
}else
{
ll cd=gcd(ans,life[i]/acc[i]);
ans*=life[i]/acc[i]/cd;
}
}
if(!flag2)
{
printf("%lld\n",ans);
}
continue;
}
if(flag)
{
ll temp=0;
for(int i=1;i<=n;i++)
{
if(life[i]%acc[i]!=0)
{
temp=max(temp,life[i]/acc[i]+1);
}else
{
temp=max(temp,life[i]/acc[i]);
}
}
printf("%lld\n",temp);
continue;
}
if(makeit())
{
continue;
}
printf("%lld\n",ex_crt());
}
return 0;
}