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
",lie());
104         else init(),printf("%lld
",excrt());
105         S.clear();
106     }                        
107     return 0;
108 }
原文地址:https://www.cnblogs.com/Qin-Wei-Kai/p/10096955.html