洛谷 P4774 [NOI2018] 屠龙勇士 题解


题目传送门

题目大意

小 D 最近在网上发现了一款小游戏。游戏的规则如下:

  • 游戏的目标是按照编号 \(1 \rightarrow n\) 顺序杀掉 \(n\) 条巨龙,每条巨龙拥有一个初始的生命值 \(a_i\) 。同时每条巨龙拥有恢复能力,当其使用恢复能力时,它的生命值就会每次增加 \(p_i\) ,直至生命值非负。只有在攻击结束后且当生命值 恰好\(0\) 时它才会死去。
  • 游戏开始时玩家拥有 \(m\) 把攻击力已知的剑,每次面对巨龙时,玩家只能选择一把剑,当杀死巨龙后这把剑就会消失,但作为奖励,玩家会获得全新的一把剑。

小 D 觉得这款游戏十分无聊,但最快通关的玩家可以获得 ION2018 的参赛资格,于是小 D 决定写一个笨笨的机器人帮她通关这款游戏,她写的机器人遵循以下规则:

  • 每次面对巨龙时,机器人会选择当前拥有的,攻击力不高于巨龙初始生命值中攻击力最大的一把剑作为武器。如果没有这样的剑,则选择 攻击力最低 的一把剑作为武器。
  • 机器人面对每条巨龙,它都会使用上一步中选择的剑攻击巨龙固定的 \(x\) 次,使巨龙的生命值减少 \(x \times ATK\)
  • 之后,巨龙会不断使用恢复能力,每次恢复 \(p_i\) 生命值。若在使用恢复能力前或某一次恢复后其生命值为 \(0\) ,则巨龙死亡,玩家通过本关。

那么显然机器人的攻击次数是决定能否最快通关这款游戏的关键。小 D 现在得知了每条巨龙的所有属性,她想考考你,你知道应该将机器人的攻击次数 \(x\) 设置为多少,才能用最少的攻击次数通关游戏吗?
当然如果无论设置成多少都无法通关游戏,输出 \(-1\) 即可。
数据范围: \(n\le 10^5\)\(a_i\le10^{12}\)\(\operatorname{lcm}(p_i)\le 10^{12}\),所有武器的攻击力 \(\le 10^6\)

题目解析

首先使用平衡树(或者是 multiset)求出打每一只巨龙所用的剑的伤害,显然打每一只巨龙所用的剑的伤害是一个固定的值。设打第 \(i\) 只巨龙所用的剑的伤害为 \(atk_i\)
然后就相当于解下面的同余方程组:

\[\left\{ \begin{aligned} atk_1x & \equiv a_1\pmod {p_1} \\ atk_2x & \equiv a_2\pmod {p_2} \\ \vdots & \\ atk_nx & \equiv a_n\pmod {p_n} \\ \end{aligned} \right. \]

方法一
显然每一个方程都是线性同余方程,所以可以解出这 \(n\) 个方程,就可以直接使用 exCRT 进行求解。
方法二
还是考虑两两合并方程,然后转化为一元二次不定方程求解。

这里采用方法一

#include<set> 
#include<cstdio>
#define db double
#define gc getchar
#define pc putchar
#define U unsigned
#define ll long long
#define ld long double
#define ull unsigned long long
#define Tp template<typename _T>
#define Me(a,b) memset(a,b,sizeof(a))
Tp _T mabs(_T a){ return a>0?a:-a; }
Tp _T mmax(_T a,_T b){ return a>b?a:b; }
Tp _T mmin(_T a,_T b){ return a<b?a:b; }
Tp void mswap(_T &a,_T &b){ _T tmp=a; a=b; b=tmp; return; }
Tp void print(_T x){ if(x<0) pc('-'),x=-x; if(x>9) print(x/10); pc((x%10)+48); return; }
#define EPS (1e-7)
#define INF (0x7fffffff)
#define LL_INF (0x7fffffffffffffff)
#define maxn 100039
#define maxm
#define MOD
#define Type long long
#ifndef ONLINE_JUDGE
//#define debug
#endif
using namespace std;
Type read(){
	char c=gc(); Type s=0; int flag=0;
	while((c<'0'||c>'9')&&c!='-') c=gc(); if(c=='-') c=gc(),flag=1;
	while('0'<=c&&c<='9'){ s=(s<<1)+(s<<3)+(c^48); c=gc(); }
	if(flag) return -s; return s;
}
int n,m; ll a[maxn],b[maxn],p[maxn],at[maxn],tmp,minx; multiset<ll> s,E;
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; return;
}
ll mul(ll x,ll y,ll p){
	ll tmp,res=0,f=1; if(x<0) x=-x,f=-f; if(y<0) y=-y,f=-f; tmp=x;
	while(y){
		if(y&1) res=(res+tmp)%p;
		tmp<<=1; tmp%=p; y>>=1;
	} return res*f;
}
ll gcd(ll x,ll y){ if(!x||!y) return x|y; return gcd(y,x%y); }
ll lcm(ll x,ll y){ return x/gcd(x,y)*y; }
void work(){
	n=read(); m=read(); s=E; minx=-1; int i;
	for(i=1;i<=n;i++) b[i]=read(); for(i=1;i<=n;i++) p[i]=read();
	for(i=1;i<=n;i++) at[i]=read(); for(i=1;i<=m;i++) tmp=read(),s.insert(tmp);
	for(i=1;i<=n;i++){
		auto it=s.upper_bound(b[i]); if(it!=s.begin()) it--;
		a[i]=*it; s.insert(at[i]); s.erase(it);
	}
	for(i=1;i<=n;i++) minx=mmax(minx,(b[i]+a[i]-1)/a[i]),a[i]%=p[i],b[i]%=p[i];
	ll g,m,ans,x,y,c,lc;
	for(i=1;i<=n;i++){
		g=gcd(a[i],p[i]); if(b[i]%g){ puts("-1"); return; } exgcd(a[i],p[i],x,y);
		tmp=p[i]/g; x=mul(x,b[i]/g,tmp); x=(x+tmp)%tmp; a[i]=x; p[i]=tmp;
	} m=p[1]; ans=a[1];
	for(i=2;i<=n;i++){
		g=gcd(m,p[i]); c=a[i]-ans; if(c%g){ puts("-1"); return; }
		exgcd(m,p[i],x,y); tmp=p[i]/g; x=(mul(x,c/g,tmp)+tmp)%tmp;
		lc=lcm(m,p[i]); ans=(mul(x,m,lc)+ans)%lc; m=lc; ans=(ans+lc)%lc;
	} print(ans>=minx?ans:m*((minx-ans+m-1)/m)+ans),pc('\n'); return;
}
int main(){
	//freopen("1.in","r",stdin);
	//freopen(".out","w",stdout);
	int T=read(); while(T--) work(); return 0;
}
posted @ 2022-06-27 20:36  jiangtaizhe001  阅读(53)  评论(0编辑  收藏  举报