「NOI2018」屠龙勇士

image
image
\(link\)

分析:

\(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\)出来的答案是不合题意的 所以特殊搞一搞
image

这样\(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;
}
posted @ 2022-01-20 10:00  EschatonRin  阅读(14)  评论(0)    收藏  举报