「NOI2018」屠龙勇士
分析:
\(Excrt\)
用\(multiset\) 就可以快速求出要用的剑了 或者平衡树
问题转化成\(:\)求同余方程组
\[\begin{cases}
Atk_1x≡Hp_1~(mod~p_1)\\
Atk_2x≡Hp_2~(mod~p_2)\\
...\\
Atk_nx≡Hp_n~(mod~p_n)
\end{cases}
\]
的最小非负整数的解
观察数据 发现存在\(Atk_i>p_i\)的情况 这样\(excrt\)出来的答案是不合题意的 所以特殊搞一搞

这样\(ans=max\{\frac{Hp_i}{Atk_i}\}\) 向上取整
其他\(Excrt\) 但这个带系数 就要再拓展
前\(i-1\)个方程通解为 \(x+kt\) 最开始可设为\(x=0,t=1\)
那么要求
\[Atk_i(x+kt)≡Hp_i~(mod~p_i)
\]
拆开移项
\[Atk_it\times k≡Hp_i-Atk_ix~(mod~p_i)
\]
未知的只有\(k\) 那么设\(A=Atk_it ,B=Hp_i-Atk_ix,C=p_i\)
\((\)即\(code\)中的\(sword,HP,Recov)\)
那就是解同余方程 \(Ak≡B~(mod~C)\) \(exgcd\)即可
要用龟速乘 以及得出的是最小非负数解 判一下 \(0\)
CODE:
点击查看代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<set>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
const int N=1e6+5;
int T,n,m;
ll a[N],b[N],p[N],add[N],atk[N];
multiset<ll> Sword;
void Pre()
{
Sword.clear();
multiset<ll>:: iterator it;
for(int i=1;i<=m;i++)
Sword.insert(atk[i]);
for(int i=1;i<=n;i++)
{
it=Sword.upper_bound(b[i]);
if(it!=Sword.begin()) it--;
a[i]=*it;
Sword.erase(it);
Sword.insert(add[i]);
}
}
ll mul(ll a,ll k,ll Mod)
{
a=(a%Mod+Mod)%Mod;
k=(k%Mod+Mod)%Mod;
ll res=0;
while(k)
{
if(k&1) (res+=a)%=Mod;
k>>=1;
(a+=a)%=Mod;
}
return res;
}
ll exgcd(ll a,ll b,ll &x,ll &y)
{
if(b==0)
{
x=1;
y=0;
return a;
}
ll res=exgcd(b,a%b,x,y);
ll k=x;
x=y;
y=k-a/b*y;
return res;
}
ll Excrt()
{
ll ans=0,t=1,sword,HP,Recov,X,Y;
for(int i=1;i<=n;i++)
{
if(p[i]==1)
{
if(b[i]%a[i]) ans=max(ans,b[i]/a[i]+1);
else ans=max(ans,b[i]/a[i]);
continue;
}
sword=a[i]*t%p[i];
HP=((b[i]-a[i]*ans)%p[i]+p[i])%p[i];
Recov=p[i];
ll gcd=exgcd(sword,Recov,X,Y);
if(HP%gcd) return -1;
Recov/=gcd;
X=mul(X,HP/gcd,Recov);
ans+=X*t; t*=Recov;
ans%=t;
}
return ans?ans:t;
}
int main(){
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%lld",&b[i]);
for(int i=1;i<=n;i++)
scanf("%lld",&p[i]);
for(int i=1;i<=n;i++)
scanf("%lld",&add[i]);
for(int i=1;i<=m;i++)
scanf("%lld",&atk[i]);
Pre();
printf("%lld\n",Excrt());
}
return 0;
}



浙公网安备 33010602011771号