【洛谷P4774】[NOI2018] 屠龙勇士【Excrt Exgcd】

102 篇文章 1 订阅
44 篇文章 0 订阅

在这里插入图片描述
在这里插入图片描述
l i n k link link

分析:

E x c r t Excrt Excrt

m u l t i s e t multiset multiset 就可以快速求出要用的剑了 除非你想写平衡树
问题转化成 : : 求同余方程组
{ A t k 1 x ≡ H p 1   ( m o d   p 1 ) A t k 2 x ≡ H p 2   ( m o d   p 2 )    . . . A t k n x ≡ H p n   ( m o d   p n ) \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} Atk1xHp1 (mod p1)Atk2xHp2 (mod p2)  ...AtknxHpn (mod pn)
的最小非负整数的解
观察数据表 发现存在 A t k i > p i Atk_i>p_i Atki>pi的情况 这样 e x c r t excrt excrt出来的答案是不合题意的 所以特殊搞一搞
在这里插入图片描述
在这里插入图片描述

这样 a n s = m a x { H p i A t k i } ans=max\{\frac{Hp_i}{Atk_i}\} ans=max{AtkiHpi} 向上取整

其他就 E x c r t Excrt Excrt 但这个带系数 就要再拓展
i − 1 i-1 i1个方程通解为 x + k t x+kt x+kt 最开始可设为 x = 0 , t = 1 x=0,t=1 x=0,t=1
那么要求
A t k i ( x + k t ) ≡ H p i   ( m o d   p i ) Atk_i(x+kt)≡Hp_i~(mod~p_i) Atki(x+kt)Hpi (mod pi)
拆开移项
A t k i t × k ≡ H p i − A t k i x   ( m o d   p i ) Atk_it\times k≡Hp_i-Atk_ix~(mod~p_i) Atkit×kHpiAtkix (mod pi)
未知的只有 k k k 那么设 A = A t k i t , B = H p i − A t k i x , C = p i A=Atk_it ,B=Hp_i-Atk_ix,C=p_i A=AtkitB=HpiAtkixC=pi
( ( ( c o d e code code中的 s w o r d , H P , R e c o v sword,HP,Recov sword,HP,Recov 意如其名 ) ) )
那就是解同余方程 A k ≡ B   ( m o d   C ) Ak≡B~(mod~C) AkB (mod C) e x g c d exgcd exgcd即可

然后要用龟速乘 以及得出的是最小非负数解 判一下 0 0 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;
} 
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值