luoguP4774 [NOI2018]屠龙勇士

题意

考虑杀每只龙(i)时候用的剑是一定的,我们可以用multiset模拟一遍得到,设为(b_i)

发现我们要求一个(x)满足对每个(i)有:(b_i*xequiv a_ipmod{p_i})

这很像扩展中国剩余定理,但是系数不是1,于是考虑化简。

假设前(i-1)个方程的答案为(res),模数的(lcm)(M)

我们要找一个(t)满足:(b_i*(res+t*M)equiv a_ipmod{p_i})

即:(b_i*M*tequiv a_i-b_i*respmod{p_i})

这时就可以用(exgcd)

注意我们并非求最小整数解,因为有必须把每头龙都打到负血的条件,我们记一个限制,最后找到第一个满足限制的解即可。
code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
int T,n,m;
ll a[maxn],b[maxn],P[maxn],sword[maxn];
multiset<ll>::iterator it;
multiset<ll>s;
ll exgcd(ll a,ll b,ll& x,ll& y)
{
	if(!b){x=1,y=0;return a;}
	ll d=exgcd(b,a%b,x,y);
	ll z=x;x=y;y=z-(a/b)*y;
	return d;
}
inline ll mul(ll x,ll y,ll mod)
{
	ll res=0;
	while(y)
	{
		if(y&1)res=(res+x)%mod;
		x=(x+x)%mod;y>>=1;
	}
	return res;
}
inline ll solve()
{
	s.clear();
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
	for(int i=1;i<=n;i++)scanf("%lld",&P[i]);
	for(int i=1;i<=n;i++)scanf("%lld",&sword[i]);
	for(int i=1;i<=m;i++)
	{
		ll x;scanf("%lld",&x);
		s.insert(x);
	}
	for(int i=1;i<=n;i++)
	{
		it=s.upper_bound(a[i]);
		if(it!=s.begin())it--;
		b[i]=*it;s.erase(it),s.insert(sword[i]);
	}
	ll maxx=0,res=0,M=1;
	for(int i=1;i<=n;i++)
	{
		maxx=max(maxx,(a[i]-1)/b[i]+1);
		ll tmpa=mul(b[i],M,P[i]),tmpb=P[i],tmpc=((a[i]-mul(b[i],res,P[i]))%P[i]+P[i])%P[i];
		ll x,y,d=exgcd(tmpa,tmpb,x,y);
		x=(x%P[i]+P[i])%P[i];
		if(tmpc%d)return -1;
		res+=mul(x,tmpc/d,P[i]/d)*M;M*=P[i]/d;
		res=(res%M+M)%M;
	}
	if(res<maxx)res+=((maxx-res-1)/M+1)*M;
	return res;
}
int main()
{
	scanf("%d",&T);
	while(T--)printf("%lld
",solve());
	return 0;
}
原文地址:https://www.cnblogs.com/nofind/p/11933142.html