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


题目传送门

题目大意

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

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

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

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

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

题目解析

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

{atk1xa1(modp1)atk2xa2(modp2)atknxan(modpn)

方法一
显然每一个方程都是线性同余方程,所以可以解出这 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 @   jiangtaizhe001  阅读(71)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 推荐几款开源且免费的 .NET MAUI 组件库
· 实操Deepseek接入个人知识库
· 易语言 —— 开山篇
· Trae初体验
点击右上角即可分享
微信分享提示