[NOI 2018] 屠龙勇士 做题笔记
传送门
前言
我是唐诗,考场没开这题。导致看都没看,直接考试策略大失误。其实知道了这题是个扩展中国剩余定理之后就很好做了。
题意
非常奇妙。
小 D 最近在网上发现了一款小游戏。游戏的规则如下:
- 游戏的目标是按照编号
顺序杀掉 条巨龙,每条巨龙拥有一个初始的生命值 。同时每条巨龙拥有恢复能力,当其使用恢复能力时,它的生命值就会每次增加 ,直至生命值非负。只有在攻击结束后且当生命值 恰好 为 时它才会死去。 - 游戏开始时玩家拥有
把攻击力已知的剑,每次面对巨龙时,玩家只能选择一把剑,当杀死巨龙后这把剑就会消失,但作为奖励,玩家会获得全新的一把剑。
小 D 觉得这款游戏十分无聊,但最快通关的玩家可以获得 ION2018 的参赛资格,于是小 D 决定写一个笨笨的机器人帮她通关这款游戏,她写的机器人遵循以下规则:
- 每次面对巨龙时,机器人会选择当前拥有的,攻击力不高于巨龙初始生命值中攻击力最大的一把剑作为武器。如果没有这样的剑,则选择 攻击力最低 的一把剑作为武器。
- 机器人面对每条巨龙,它都会使用上一步中选择的剑攻击巨龙固定的
次,使巨龙的生命值减少 。 - 之后,巨龙会不断使用恢复能力,每次恢复
生命值。若在使用恢复能力前或某一次恢复后其生命值为 ,则巨龙死亡,玩家通过本关。
那么显然机器人的攻击次数是决定能否最快通关这款游戏的关键。小 D 现在得知了每条巨龙的所有属性,她想考考你,你知道应该将机器人的攻击次数
当然如果无论设置成多少都无法通关游戏,输出
思路
part 1
首先,我们很容易能够知道。其实每一只龙
每次面对巨龙时,机器人会选择当前拥有的,攻击力不高于巨龙初始生命值中攻击力最大的一把剑作为武器。如果没有这样的剑,则选择 攻击力最低 的一把剑作为武器。
这就说明,每一次选剑的策略是一定的,那么每一只龙就一定有一把对应用来砍它的剑 multiset
。显然 multiset
会好写一些。
这一部分的处理代码就长这样(其中 s 就是 multiset
):
for(int i=1;i<=n;i++){
auto place=s.upper_bound(a[i]);
if(place!=s.begin()){
place--;
}
b[i]=*place;
s.erase(place);
s.insert(t[i]);
maxn=max(maxn,(a[i]-1)/b[i]+1);
}
part 2
然后我们进入核心阶段。由于上面我们找到了每一只龙对应的剑
那么多条龙也是一样的,我们就可以列出方程组:
part 3
现在已经能看出来,这题明显可以用 ExCrt 求解。但是这里还有个系数
为什么?首先我们可以知道
然后,我们把上面那个式子移项。就能发现这个式子其实就等价于:
于是直接扩展欧几里得求解。注意计算过程会爆 long long
。建议直接使用 __int128
。
AC code
#include<bits/stdc++.h>
#define int __int128
using namespace std;
namespace WYL{
const int N=1e5+10;
multiset<int> s;
int T,n,m,a[N],b[N],p[N],t[N],maxn=0;
inline int read(){
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9')
x=x*10+ch-'0',ch=getchar();
return x*f;
}
inline void Write(int x){
if(x<0)
putchar('-'),x=-x;
if(x>9)
Write(x/10);
putchar(x%10+'0');
return;
}
int gsc(int a,int b,int mod){
int ans=0;
while(b){
if(b&1){
ans=(ans+a)%mod;
}
a=(a+a)%mod;
b/=2;
}
return ans;
}
void exgcd(int a,int b,int &x,int &y,int &gcd) {
if(!b){
x=1;
y=0;
gcd=a;
}else{
exgcd(b,a%b,y,x,gcd);
y-=(a/b)*x;
}
return;
}
int solve(){
int ans=0,lcm=1,x,y,Gcd,yi,er,san;
for(int i=1;i<=n;i++){
// yi=gsc(b[i],lcm,p[i]);
yi=b[i]*lcm%p[i];
// cout<<"pass1"<<endl;
er=p[i];
san=(a[i]-b[i]*ans%p[i]+p[i])%p[i];
// cout<<"pass2"<<endl;
exgcd(yi,er,x,y,Gcd);
// cout<<"pass3"<<endl;
x=(x%er+er)%er;
if(san%Gcd){
return -1;
}
// lcm=lcm*(er/Gcd);
ans=(ans+(san/Gcd)*x%(er/Gcd)*lcm%(lcm*=er/Gcd))%lcm;
// ans=(ans+gsc(san/Gcd,x,er/Gcd)*lcm%(lcm*er/Gcd))%lcm;
}
if(ans<maxn){
ans=ans+((maxn-ans-1)/lcm+1)*lcm;
}
return ans;
}
int main(){
T=read();
while(T--){
s.clear();
n=read();
m=read();
maxn=0;
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++){
t[i]=read();
}
for(int i=1;i<=m;i++){
int opt;
opt=read();
s.insert(opt);
}
// cout<<"pass"<<endl;
for(int i=1;i<=n;i++){
auto place=s.upper_bound(a[i]);
if(place!=s.begin()){
place--;
}
b[i]=*place;
s.erase(place);
s.insert(t[i]);
maxn=max(maxn,(a[i]-1)/b[i]+1);
}
// cout<<ExCRT()<<endl;
Write(solve());
puts("");
}
return 0;
}
}
signed main(){
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
// freopen("dragon.in","r",stdin);
// freopen("dragon.out","w",stdout);
WYL::main();
return 0;
}
本文作者:Chillturtle
本文链接:https://www.cnblogs.com/OluMel/p/18502096
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步