NOI2018/LuoGu P4774屠龙勇士
本蒟蒻终于屠龙成功!!
作为一个我的世界的资深玩家,对屠龙有着无比的信仰和热情!!
首先吐槽一下这只信息龙,它变成负HP后居然不会死,只有他自己回血回到了0HP才算死23333
来我们来看看题意描述,国赛的题意描述略长
这道题其实没有那么的可怕,当时我写完excrt就有学长推荐我来写
题目描述
小 D 最近在网上发现了一款小游戏。游戏的规则如下: • 游戏的目标是按照编号 1~n 顺序杀掉 n 条巨龙,每条巨龙拥有一个初始的生命 值 a i 。同时每条巨龙拥有恢复能力,当其使用恢复能力时,它的生命值就会每 次增加 p i ,直至生命值非负。只有在 . 攻 . 击 . 结 . 束 . 后且当生命值 . 恰 . 好为 0 时它才会 死去。 • 游戏开始时玩家拥有 m 把攻击力已知的剑,每次面对巨龙时,玩家只能选择一把剑,当杀死巨龙后这把剑就会消失,但作为奖励,玩家会获得全新的一把剑。小 D 觉得这款游戏十分无聊,但最快通关的玩家可以获得 ION2018 的参赛资格,于是小 D 决定写一个笨笨的机器人帮她通关这款游戏,她写的机器人遵循以下规则:• 每次面对巨龙时,机器人会选择当前拥有的,攻击力不高于巨龙初始生命值中.攻.击.力.最.大的一把剑作为武器。如果没有这样的剑,则选择..击.力.最.低的一把剑作为武器。• 机器人面对每条巨龙,它都会使用上一步中选择的剑攻击巨龙.固.定.的 x 次,使巨龙的生命值减少 x × ATK 。• 之后,巨龙会不断使用恢复能力,每次恢复 p i 生命值。若在使用恢复能力前或某一次恢复后其生命值为 0 ,则巨龙死亡,玩家通过本关。那么显然机器人的.攻.击.次.数是决定能否最快通关这款游戏的关键。小 D 现在得知 了每条巨龙的所有属性,她想考考你,你知道应该将机器人的攻击次数 x 设置为多少,才能用最少的攻击次数通关游戏吗?当然如果无论设置成多少都无法通关游戏,输出-1 即可。
输入格式
从文件 dragon.in 中读入数据。 第一行一个整数 T ,代表数据组数。 接下来 T 组数据,每组数据包含 5 行。 • 每组数据的第一行包含两个整数,n 和 m ,代表巨龙的数量和初始剑的数量; • 接下来一行包含 n 个正整数,第 i 个数表示第 i 条巨龙的初始生命值 a i ; • 接下来一行包含 n 个正整数,第 i 个数表示第 i 条巨龙的恢复能力 p i ; • 接下来一行包含 n 个正整数,第 i 个数表示杀死第 i 条巨龙后奖励的剑的攻击 力; • 接下来一行包含 m 个正整数,表示初始拥有的 m 把剑的攻击力。
输出格式
输出到文件 dragon.out 中。 一共 T 行。 第 i 行一个整数,表示对于第 i 组数据,能够使得机器人通关游戏的最小攻击次数 x ,如果答案不存在,输出-1。
样例一
input
2 3 3 3 5 7 4 6 10 7 3 9 1 9 1000 3 2 3 5 6 4 8 7 1 1 1 1 1
output
59 -1
样例 1 解释
第一组数据: • 开始时拥有的剑的攻击力为 {1,9,10},第 1 条龙生命值为 3,故选择攻击力为 1的剑,攻击 59 次,造成 59 点伤害,此时龙的生命值为-56,恢复 14 次后生命值 恰好为 0,死亡。• 攻击力为 1 的剑消失,拾取一把攻击力为 7 的剑,此时拥有的剑的攻击力为{7,9,10},第 2 条龙生命值为 5,故选择攻击力为 7 的剑,攻击 59 次,造成 413点伤害,此时龙的生命值为-408,恢复 68 次后生命值恰好为 0,死亡。• 此时拥有的剑的攻击力为 {3,9,10},第 3 条龙生命值为 7,故选择攻击力为 3 的 剑,攻击 59 次,造成 177 点伤害,此时龙的生命值为-170,恢复 17 次后生命值恰好为 0,死亡。 • 没有比 59 次更少的通关方法,故答案为 59。 第二组数据: • 不存在既能杀死第一条龙又能杀死第二条龙的方法,故无法通关,输出-1。
子任务
特性 1 是指:对于任意的 i,a i ≤ p i 。 特性 2 是指:LCM(p i ) ≤ 10 6 即所有 p i 的最小公倍数不大于 10 6 。 对于所有的测试点,T ≤ 5 ,所有武器的攻击力 ≤ 10 6 ,所有 p i 的.最.小.公.倍.数≤ 10 12 。
你所用到的中间结果可能很大,注意保存中间结果的变量类型。
限制与约定
时间限制:2s2s
空间限制:512MB
好了我们来看看这道题的思路是什么
首先题要读懂!!我就是读错了题导致WA了三个点(数据好水居然只WA了三个)
通过题干可得(heal是回复,atk是攻击)
ATK*x = hp + heal*y
然后呢我们mod一个heal
ATK*x = hp + heal*y(% heal})
$AT{K_i}*x = h{p_i}(\% hea{l_i})$
就很明了了,是一个ex中国剩余定理,因为x是固定的,取模数不一定是质数
现在的问题是x前面有一个讨厌的系数,它应该怎么弄掉,对,把它除到右面就好了
$x = h{p_i}*ATK_i^{ - 1}(\% hea{l_i})$
很明显就是求一个ATK关于heali的逆元(注意先约分再求逆元,除了零以外,所有两个互质的数之间都存在逆元)
做题的好习惯是看数据范围,看到前几组数据了吗,显然攻击力恢复力全是1,excrt是过不了的,那也很简单,只要把它砍成刚变成负值就好了,也很简单
但是看到后面的数据范围十分庞大,显然在选剑的时候回TLE,那么就需要一些数据结构,用multiset就好(不用set因为有重复元素)
这里用到了一个函数叫upperbound,它会返回最后一个大于等于它的数的迭代器,再- -就是选的剑,这个是set的存储特点,注意每次都clear()一下就OK
上代码!
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #include<set> 5 using namespace std; 6 typedef long long ll; 7 #define N 100011 8 #define M 100011 9 #define rg register 10 ll T,n,m,x,y;bool fl=1; 11 ll hp[N],reward[N],heal[N],sword,r[N],a[N]; 12 multiset<ll>S; 13 namespace ldy 14 { 15 ll gcd(ll a,ll b){return b?gcd(b,a%b):a;} 16 ll lcm(ll a,ll b){return a/gcd(a,b)*b;} 17 ll ksc(ll a,ll b,ll mod) 18 { 19 ll fina=0,kk=1; 20 if(a<0)a=-a,kk=-kk; 21 if(b<0)b=-b,kk=-kk; 22 while(b) 23 { 24 if(b%2)fina=(fina+a)%mod; 25 b>>=1,a=(a+a)%mod; 26 } 27 return fina%mod*kk; 28 } 29 ll read() 30 { 31 ll f=1,x=0; 32 char ss=getchar(); 33 while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();} 34 while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();} 35 return f*x; 36 } 37 void ex_gcd(ll a,ll b) 38 { 39 if(!b)x=1,y=0; 40 else 41 { 42 ex_gcd(b,a%b); 43 ll tt=x;x=y;y=tt-a/b*x; 44 } 45 } 46 ll exgcd(ll a,ll b,ll c) 47 { 48 ll gc=gcd(a,b); 49 if(c%gc)return -1; 50 a/=gc,b/=gc,c/=gc; 51 ex_gcd(a,b); 52 return (ksc(x,c,b)+b)%b; 53 } 54 } 55 using namespace ldy; 56 void init() 57 { 58 for(rg int i=1;i<=n;i++) 59 { 60 multiset<ll>::iterator it =S.upper_bound(hp[i]); 61 if(it!=S.begin())it--; 62 ll atk=*it; 63 ll gc=gcd(atk,heal[i]); 64 atk/=gc,hp[i]/=gc,heal[i]/=gc; 65 a[i]=heal[i],r[i]=ksc(hp[i],exgcd(atk,heal[i],1),heal[i]); 66 S.erase(it),S.insert(reward[i]); 67 } 68 } 69 ll excrt() 70 { 71 for(int i=1;i<n;i++) 72 { 73 ll k0=exgcd(a[i],a[i+1],r[i+1]-r[i]); 74 if(k0==-1)return -1; 75 r[i+1]=a[i]*k0+r[i]; 76 a[i+1]=lcm(a[i],a[i+1]); 77 } 78 return exgcd(1,a[n],r[n]); 79 } 80 ll lie() 81 { 82 ll ans=-1; 83 for(int i=1;i<=n;i++) 84 { 85 multiset<ll>::iterator it =S.upper_bound(hp[i]); 86 if(it!=S.begin())it--; 87 ll atk=*it; 88 if(hp[i]%atk)ans=max(ans,hp[i]/atk+1); 89 else ans=max(ans,hp[i]/atk); 90 S.erase(it),S.insert(reward[i]); 91 } 92 return ans; 93 } 94 int main() 95 { 96 for(int tt=read();tt>0;tt--) 97 { 98 n=read(),m=read();fl=1; 99 for(rg int i=1;i<=n;i++)hp[i]=read(); 100 for(rg int i=1;i<=n;i++){heal[i]=read();if(heal[i]!=1)fl=0;} 101 for(rg int i=1;i<=n;i++)reward[i]=read(); 102 for(rg int i=1;i<=m;i++){sword=read();S.insert(sword);} 103 if(fl)printf("%lld\n",lie()); 104 else init(),printf("%lld\n",excrt()); 105 S.clear(); 106 } 107 return 0; 108 }