NOI2018 DAY2 T1 屠龙勇士(拓展中国剩余定理)

到现在我才知道upper_bound和lower_bound的区别。。。

如果为升序,lower_bound指的是大于等于x的数,而upper_bound指的是大于x的数。

同理为降序,lower_bound指的是小于等于x的数,而upper_bound指的是小于x的数。

再来介绍一下拓展中国剩余定理。

本蒟蒻的证明。。。若有问题请大家提出。。https://blog.csdn.net/zzk_233/article/details/82901942

对于剑的攻击力需要先用拓展gcd求出逆元乘到右边,之后就是模板。。当然还有一些特殊情况在代码中说明了。

#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<set>
using namespace std;
typedef long long ll;
ll x,y,ans,M,maxn,k;
int n,m;
multiset<ll>v;
ll c[100005];
ll p[100005];
ll v1[100005];
ll ex_gcd(ll a,ll b)
{
	if(b==0)
	{
		x=1,y=0;
		return a;
	}
	ll r=ex_gcd(b,a%b);
	ll t=x;
	x=y;
	y=t-a/b*x;
	return r;
}
ll mul(ll a,ll b,ll mode)
{
	ll s=0;
	while(b)
	{
		if(b%2==1)
		{
			s=(s+a)%mode;
		}
		a=(a+a)%mode;
		b=b/2;
	}
	return s;
}
int flag;
int main()
{
	int T;
	scanf("%d",&T);
	multiset<ll>::iterator ty;
	while(T--)
	{
		scanf("%d%d",&n,&m);
		for(int i = 1;i <= n;i++)
		{
			scanf("%lld",&c[i]);
		}
		for(int i = 1;i <= n;i++)
		{
			scanf("%lld",&p[i]);
		}
		for(int i = 1;i <= n;i++)
		{
			scanf("%lld",&v1[i]);
		}
		v.clear();
		for(int i = 1;i <= m;i++)
		{
			ll x;
			scanf("%lld",&x);
			v.insert(x);
		}
		maxn=0,ans=0,M=1,k;
 
		for(int i = 1;i <= n;i++)
		{
			ty=v.upper_bound(c[i]);
			if(ty!=v.begin())ty--;
			k=*ty;
			v.erase(ty);
			v.insert(v1[i]);
			maxn=max(maxn,(c[i]-1)/k+1);
			k%=p[i];c[i]%=p[i];
			if(!k&&c[i])//攻击力整除血量但是回血不整除,显然无解 
			{
				printf("-1\n");
				flag=1;
				break;
			}
			if(!k&&!c[i])//攻击力整除血量而且回血也整除,显然一定有解
			{
				continue;
			} 
			ll gg=ex_gcd(k,p[i]);
			if(c[i]%gg)
			{
				printf("-1\n");
				flag=1;
				break;
			}
			p[i]/=gg;c[i]/=gg;k/=gg;
			x=(x%p[i]+p[i])%p[i];
			c[i]=mul(c[i],x,p[i]);
			gg=ex_gcd(M,p[i]);
			if((c[i]-ans)%gg)
			{
				printf("-1\n");
				flag=1;
				break;
			}
			M/=gg;M*=p[i];
			x=(x%M+M)%M;
			ans+=mul(mul(M/p[i],((c[i]-ans)%M+M)%M,M),x,M)%M;
		}
		if(flag==1)
		{
			flag=0;
			continue;
		}
		printf("%lld\n",ans>=maxn?ans:maxn);
	}
	
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值