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

Description

Solution

首先,我们使用的武器和得到的武器是一定的,与选择的攻击次数无关,所以我们只需要一个\(multiset\)模拟整个过程就能预处理面对每条龙所选择的武器。

容易发现要让巨龙在回血一定次数后死亡就类似于一个取模操作,设面对第\(i\)条龙是武器的攻击力为\(atk_i\),于是有\(x\times atk_i\equiv a_i\pmod {p_i}\)

于是原问题就等价于一个方程组。。。等价吗?

\(a_i> p_i\)的时候,攻击\(k\)次有可能虽然满足上面这个方程,但是\(x\times atk_i<a_i\)!,这并不会让巨龙死亡。

怎么办呢?增加一个限制条件?仔细分析数据范围,你会发现:所有没有保证\(a_i\le p_i\)的数据都保证\(p_i=1\),这种情况下答案显然是\(Max_{i=1}^{n}\lceil \frac{a_i}{atk_i} \rceil\),直接特判即可。

回到上面的方程,\(x\times atk_i\equiv a_i\pmod {p_i}\),我们很想将它化成一个\(exCRT\)能求解的形式,即\(x\equiv y\pmod{p_i}\),于是将原方程化为\(x\times atk_i+b\times p_i=a_i\)\(exgcd\)求出它的解即可改写成一次项系数为\(0\)的方程,注意这个方程可能无解,需要特判。

一波操作之后,我们终于化成了我们熟悉的形式,\(exCRT\)求解即可。

Code

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+10;
int T,n,m;
ll a[N],p[N],at[N],r[N],v[N<<1];
inline void init(){
	multiset<ll> s;
	for(int i=1;i<=m;++i) s.insert(v[i]);
	for(int i=1;i<=n;++i){
		multiset<ll>::iterator it=s.upper_bound(a[i]);
		if(it!=s.begin()) it--;
		at[i]=*it;s.erase(it);
		s.insert(v[m+i]); 
	} 
}
inline void exgcd(ll a,ll b,ll &x,ll &y){
	if(!b){x=1;y=0;return ;}
	exgcd(b,a%b,x,y);
	ll t=x;x=y;y=t-a/b*y;
}
int flag;
inline ll add(ll x,ll y,ll mod){return (x+y)%mod;}
inline ll dec(ll x,ll y,ll mod){return ((x-y)%mod+mod)%mod;}
inline ll mul(ll x,ll y,ll mod){
	ll z=(long double)x/mod*y;
	return ((x*y-z*mod)%mod+mod)%mod;
}
inline void getans(int tp,ll a,ll b,ll c,ll &x,ll &y,ll &mod){
	ll g=__gcd(a,b);
	if(c%g){x=-1;return ;}
	if(tp==1) mod/=g;
	exgcd(a,b,x,y);
	ll t=c/g;
	x=mul(x,t,mod);y=mul(y,t,mod);
}

inline ll gcd(ll x,ll y){return (y==0)?x:gcd(y,x%y);}
inline ll excrt(){
	for(int i=1;i<=n;++i){
		ll y;
		flag=0;getans(1,at[i],p[i],a[i],r[i],y,p[i]);
		if(r[i]==-1) return -1;
	}
	for(int i=2;i<=n;++i){		 
		ll x,y;
		ll nmod=(p[i-1]/gcd(p[i-1],p[i]))*p[i];
		getans(2,p[i-1],p[i],dec(r[i],r[i-1],nmod),x,y,nmod);
		p[i]=nmod;r[i]=add(mul(x,p[i-1],nmod),r[i-1],nmod);
		if(r[i]==-1) return -1;
	}
	return r[n];
}
int main(){
	scanf("%d",&T);
	while(T--){
		scanf("%d%d",&n,&m);
		int flag=0;
		for(int i=1;i<=n;++i) scanf("%lld",&a[i]);
		for(int i=1;i<=n;++i){
			scanf("%lld",&p[i]);
			if(p[i]!=1) flag=1;
		}
		for(int i=1;i<=n;++i) scanf("%lld",&v[m+i]);
		for(int i=1;i<=m;++i) scanf("%lld",&v[i]);
		init();
		if(!flag){
			ll ans=0;
			for(int i=1;i<=n;++i){
				ll t=a[i]/at[i];if(a[i]%at[i]) ++t;
				ans=max(ans,t);
			}
			printf("%lld\n",ans);
			continue;
		}
		ll ans=excrt();
		if(ans==-1) puts("-1");
		else printf("%lld\n",ans);
	}
	return 0;
}
posted @ 2021-02-01 22:12  cjTQX  阅读(79)  评论(0编辑  收藏  举报