P4774 [NOI2018]屠龙勇士(exCRT)
题目链接:传送门
思路:
首先根据题意我们可以算出来对于每一条龙使用的武器攻击力(这个使用set和upper_bound成员函数很容易实现)。
现在我们价假设第 i 条龙的生命值为 h[i],恢复能力为 p[i],此时使用的武器攻击力为 g[i], 那么我们攻击次数 x 需要满足这样的方程式 (h[i]−x∗g[i])%p[i]=0 ,且 h[i]−x∗g[i]≤0。
现在我们有n个方程我们根据所有的第二个限制条件求出来 x的最小值。接下来就是对所有的第一个方程求出x的值即可。很容易看出第一个方式即 x∗g[i]=h[i] ( mod p[i])
我们把等式两边全除以 g[i],h[i],p[i]的gcd,得到方程 x∗g[i]/gcd=h[i]/gcd ( mod p[i]/gcd) 此时我们将等式两边同时乘以 g[i]/gcd关于 p[i]/gcd的逆元,这样方程就是一个一次同余方程了。 如果 g[i]/gcd关于 p[i]/gcd的逆元不存在,那么证明两个数不互质(此时 g[i]/gcd与 p[i]/gcd 互质),不等式无解,直接输出-1。否则最终得到n个一次同余方程,那么用exCRT解出 a,m即可,因为 a是模 m的,所以如果a小于x的最小值的话,就让a增加m的倍数即可。
但是要注意的是,这题数据都在long long 的范围,中间相乘可能会爆long long ,那么此时要有龟速乘法。
本来想做一道CRT合并的题,谁知道只是个exCRT的,第一次乘法爆long long了导致只拿了85分,
爆long long 这可能就是有几次做CRT的题思路是对的,但总是wa的原因吧。
代码:
#include<bits/stdc++.h>
#define mset(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
const int N=1e5+100;
const int inf=0x3f3f3f3f;
ll h[N],p[N],g[N],s[N];
multiset<ll> mmp;
ll qpow(ll a,ll b,ll m)
{
ll ans=1;
while(b)
{
if(b&1) ans=(ans*a)%m;
a=a*a%m;
b>>=1;
}
return ans;
}
ll mul(ll a,ll b,ll m)
{
ll ans=0,det=1;
if( a < 0){ det*=-1; a=-a; }
if(b < 0){ det*=-1; b=-b; }
while(a)
{
if(a&1) ans=(ans+b)%m;
b=b*2%m;
a/=2;
}
return (ans*det%m+m)%m;
}
long long extend_gcd(long long a,long long b,long long &x,long long &y)
{
//函数结束后满足 x*a+y*b=gcd
if(a==0&&b==0) return -1;
//无最大公约数
if(b==0)
{
x=1;
y=0;
return a;
}
long long d=extend_gcd(b,a%b,y,x);
y-=a/b*x;
return d;
}
//*********求逆元素*******************
//ax = 1(mod n)
long long mod_reverse(long long a,long long n)
{
long long x,y;
long long d=extend_gcd(a,n,x,y);
if(d==1) return (x%n+n)%n;
else return -1;
}
bool solve(ll &m0,ll &a0,ll m,ll a)
{
long long y,x;
ll g = extend_gcd(m0,m,x,y);//得到的x是m0/g关于m1/g的逆元
if( abs(a - a0)%g )return false;
// x *= (a - a0)/g;
// x %= m/g;
x=mul(x,(a-a0)/g,m/g);//**
ll d=m0;
// a0 = (x*m0 + a0);
m0 *= m/g; //合并方程后的模数是增大的
a0=(mul(x,d,m0)+a0)%m0;//**
// a0 %= m0;
if( a0 < 0 )a0 += m0; //a0也是慢慢增大的
return true;
}
ll a[N],m[N];//模数为m余数为a
bool MLES(ll &m0,ll &a0,ll a[],ll m[],ll n) //解为 X = a0 + m0 * k
{
bool flag = true;
m0 = 1;
a0 = 0;
for(ll i = 1; i <=n; i++)
if( !solve(m0,a0,m[i],a[i]) )
{
flag = false;
break;
}
return flag;
}
int main()
{
ios::sync_with_stdio(false);cin.tie(0);
ll t;
cin>>t;
while(t--)
{
mmp.clear();
ll n,_m;
cin>>n>>_m;
for(ll i=1; i<=n; ++i) cin>>h[i];
for(ll i=1; i<=n; ++i) cin>>p[i];
for(ll i=1; i<=n; ++i) cin>>s[i];
for(ll i=1; i<=_m; ++i)
{
ll v;
cin>>v;
mmp.insert(v);
}
for(ll i=1; i<=n; ++i)
{
ll v;
auto it=mmp.upper_bound(h[i]);
if(it!=mmp.begin())
{
v=*(--it);
mmp.erase(it);
}
else
{
v=*it;
mmp.erase(it);
}
mmp.insert(s[i]);
g[i]=v;
}
ll minx=-1;
ll ok=true;
for(ll i=1; i<=n&&ok; ++i)
{
ll gcd=__gcd(h[i],__gcd(p[i],g[i]));
h[i]/=gcd;
p[i]/=gcd;
g[i]/=gcd;
if(!g[i] && !h[i]) continue;
if(!g[i]&&h[i]) {
ok=false;
break;
}
ll v=(h[i]+g[i]-1)/g[i];
minx=max(minx,v);
m[i]=p[i];
v=mod_reverse(g[i],p[i]);
if(v==-1)
{
ok=false;
break;
}
else
{
a[i]=mul(h[i],v,m[i]);//h[i]*v)%m[i];
}
}
if(!ok)
{
cout<<"-1"<<endl;
continue;
}
ll aa=1,mm=1;
if(!MLES(mm,aa,a,m,n))
{
cout<<-1<<endl;
}
else{
if(aa>=minx){
cout<<aa<<endl;
}
else{
ll v=((minx-aa)+mm-1)/mm;
cout<<aa+v*mm<<endl;
}
}
}
return 0;
}