Loading

题解 Luogu P4774 [NOI2018]屠龙勇士

题意

\(n\) 只巨龙,第 \(i\) 只的初始生命值为 \(a_i\),并且拥有一个生命恢复值 \(p_i\)。你有 \(m\) 把剑,每把剑有一个攻击力。现在你要用这 \(m\) 把剑依次杀死这 \(n\) 只巨龙。

在攻击第 \(i\) 只巨龙之前,你需要选出攻击力不大于 \(a_i\)攻击力最大的一把剑。如果这样的剑不存在,则选出攻击力最小的一把剑。设这把剑的攻击力为 \(v\),接下来:

  • 你会连续攻击巨龙 \(x\) 次,巨龙的生命值变为 \(a_i -x\times v\)
  • 巨龙会不断恢复生命值,每次恢复 \(p_i\),直到生命值非负;
  • 此时,若巨龙生命值为 \(0\),巨龙死去;
  • 用来攻击的这把剑会消失。与此同时,如果巨龙死去,你会获得一把新的剑,攻击力可能和这一把不同

\(x\) 的最小非负整数解,或者指出 \(x\) 不存在。

\(1 \leq n,m \leq 10^5,p_i \geq 1,\operatorname{lcm}(p_i) \leq 10^{12},a_i \leq 10^{12}\)

题解

观察到用来攻击每个巨龙的剑的攻击力是固定的。设攻击第 \(i\) 只巨龙时,用到的剑攻击力为 \(b_i\)。考虑列出方程组

\[\begin{aligned}b_1x \equiv a_1 \pmod{p_1} \\ b_2x \equiv a_2 \pmod {p_2} \\ \cdots \\ b_nx \equiv a_n\pmod{p_n} \end{aligned} \]

考虑对方程进行化简,将形如 \(b_ix \equiv a_i \pmod{p_i}\) 的方程化为 \(b_ix + p_iy = a_i\) 的标准扩欧形式。

首先判断该方程是否有解。根据裴蜀定理,该方程有解的充要条件是 \(\gcd(b_i,p_i) \mid a_i\)。使用 exgcd 求出原方程的一组特解 \(x = x_0\),那么 \(x\) 的解集为 \(x = x_0 + k \dfrac{p_i}{\gcd(b_i,p_i)}\)。再化回同余方程的形式,得 \(x \equiv x_0 \pmod{\dfrac{p_i}{\gcd(b_i,p_i)}}\)

对于每个方程进行上述处理,便得到了可以使用 exCRT 的形式。设该方程组的最小非负整数解为 \(s\)。注意到 \(s\) 不一定是原问题的一个合法解,因为 \(s\) 次攻击可能不一定会使某条巨龙的生命值变为非正数。这个时候就需要将 \(s\) 不断增加 \(\operatorname{lcm}(\dfrac{p_i}{\gcd(b_i,p_i)})\)(即新方程组模数的最小公倍数),直到 \(s\) 次攻击可以把所有巨龙的生命值变为非正数。

# include <bits/stdc++.h>
# define int __int128
const int N=100010,INF=0x3f3f3f3f;

int n,m;
int a[N],p[N],b[N],c[N];
std::multiset <int> S;

int pval[N],bval[N];
int maxx;

inline int read(void){
	int res,f=1;
	char c;
	while((c=getchar())<'0'||c>'9')
		if(c=='-')f=-1;
	res=c-48;
	while((c=getchar())>='0'&&c<='9')
		res=res*10+c-48;
	return res*f;
}
void print(int x){
	if(x<0)
		putchar('-'),x=-x;
	if(x>9)
		print(x/10);
	putchar(x%10+'0');
	return;
}
inline int exgcd(int a,int b,int &x,int &y){
	if(!b){
		x=1,y=0;
		return a;
	}
	int gcd=exgcd(b,a%b,x,y),Temp;
	Temp=x,x=y,y=Temp-(a/b)*y;
	return gcd;
}
inline bool build_excrt(void){
	for(int i=1;i<=n;++i){
		int A=c[i],B=p[i],C=a[i],xz,yz;
		int gcd=exgcd(A,B,xz,yz);
		if(C%gcd)
			return false;
		bval[i]=xz*C/gcd,pval[i]=B/gcd,bval[i]%=pval[i];
	}
	return true;
}
inline int solve(void){
	if(!build_excrt()){
		return -1;
	}
	int M=pval[1],ans=bval[1];
	for(int i=2;i<=n;++i){
		int A=M,B=pval[i],C=((bval[i]-ans)%pval[i]+pval[i])%pval[i],x,y,gcd;
		gcd=exgcd(A,B,x,y);
		if(C%gcd)
			return -1;
		ans+=C/gcd*x*M;
		M=M/gcd*pval[i];
		ans=(ans%M+M)%M;
	}
	if(ans<maxx)
		ans+=((maxx-ans-1)/M+1)*M;
	return ans;
}
signed main(void){
	int T=read();
	while(T--){
		maxx=0;
		S.clear(),n=read(),m=read();
		for(int i=1;i<=n;++i)
			a[i]=read();
		for(int i=1;i<=n;++i)
			p[i]=read();
		for(int i=1;i<=n;++i)
			b[i]=read();
		while(m--)	
			S.insert(read());
		for(int i=1;i<=n;++i){
			std::multiset <int>::iterator it=S.upper_bound(a[i]);
			if(S.begin()!=it)
				--it;
			c[i]=*it,S.erase(it),S.insert(b[i]);
			maxx=std::max(maxx,(a[i]-1)/c[i]+1);
		}
		print(solve()),puts("");
	}
	return 0;
}

posted @ 2021-09-11 21:31  Meatherm  阅读(66)  评论(0编辑  收藏  举报