题解 P4774 【[NOI2018]屠龙勇士】
LeavingZzz · · 题解
\mathsf{Solution\text{ }For\text{ }P4774}
\mathsf{Description}
为了读懂这题到底想让你干什么,先从简单的看起
先不管机器人对屠龙宝刀的选择以及杀死一条恶龙之后点击就送的屠龙宝刀,现在假设面对每一条龙时你手里的刀的属性已知,这个问题似乎变得简单了一些。
因为每一条恶龙的初始血量知道,但是恶龙可以无限制的回血,必须刚好把血量砍成
那么实际上就是一个同余方程
我们设面对第
的解
首先肯定是利用扩展中国剩余定理求解
但是这题有变化的是这些方程的
首先对于原版的扩展中国剩余定理我们的解法是:
设前
将通解代入第
其中已知的量有:
移项得:
化为不定方程形式为
(框起来的是未知数部分)
使用
这时候前
前
这时候便有了前
本题
- 数据规模要求写龟速乘以免 longlong 溢出。
- 注意上面的移项步骤中的右边是
r_i-a_i\times ans ,需要调整为正值。 - 最后解出来的是最小整数解但不一定是最小的合法解,因为方程的通解是
ans+k\times lcm,k\in \mathbb{Z} , 而我们每次砍恶龙的时候必须将其血量砍为负值或者0 ,所以满足同余方程的解不一定合法,最后会特殊处理一下
那么现在问题回归了,按照题目的描述我们怎么把给出的剑按照机器人的选择配对以及杀死恶龙爆出的剑怎么处理?
可以借助
对于初始的剑,直接按照攻击力插入
然后枚举每一条龙的生命值,按照题目描述,使用
这里要注意
\mathsf{Code:}
#include<bits/stdc++.h>
#include<set>
#include<algorithm>
using namespace std;
const int maxn=100007;
typedef long long LL;
LL m[maxn],r[maxn];
LL rew[maxn],a[maxn];
inline LL R()
{
LL re;
char c;
while((c=getchar())>'9'||c<'0');
re=c-48;
while((c=getchar())>='0'&&c<='9')
re=re*10+c-48;
return re;
}
LL N,M,T;
LL g,ATK;
inline void Exgcd(LL a,LL b,LL &x,LL &y)
{
if(!b)
{
g=a;
x=1;y=0;
return ;
}
Exgcd(b,a%b,y,x);
y-=(a/b)*x;
return ;
}
inline LL X(LL ai,LL b,LL m)//龟速乘
{
LL s=0;
while(b)
{
if(b&1) s=(s+ai)%m;
ai=(ai<<1)%m;
b>>=1;
}
return s;
}
inline LL ExCRT()
{
LL ans=0,lcm=1,A,B,C,x,y;
for(int i=1;i<=N;i++)
{
A=X(lcm,a[i],m[i]);B=m[i];
C=((r[i]-X(a[i],ans,m[i]))%m[i]+m[i])%m[i];//等式右边调整为正值
Exgcd(A,B,x,y);
x=(x%m[i]+m[i])%m[i];
if(C%g) return -1;//无解
ans+=X(C/g,x,m[i]/g)*lcm;
lcm=lcm*(m[i]/g);
}
if(ans<ATK)//不是合法解
{
x=(ATK-ans)%lcm==0?(ATK-ans)/lcm:(ATK-ans)/lcm+1;//至少要多砍多少刀才是合法的
ans=ans+x*lcm;//通解形式
}
return ans;
}
int main()
{
T=R();
while(T--)
{
multiset<LL>sword;
ATK=0;//记得置0
N=R();M=R();
for(register int i=1;i<=N;i++)
r[i]=R();//初始生命值
for(register int i=1;i<=N;i++)
m[i]=R();//恢复能力
for(register int i=1;i<=N;i++)
rew[i]=R();//杀死恶龙后屠龙宝刀点击送
for(register int i=1;i<=M;i++)
sword.insert(R());//初始的剑
multiset<LL>::iterator it;
for(register int i=1;i<=N;i++)
{
it=sword.upper_bound(r[i]);//使用upperbound(想想为什么)
if(it!=sword.begin()) it--;
a[i]=(*it);
sword.erase(it);//这句表示删除迭代器it所指向的位置,如果写 sword.erase(*it);则表示删除所有值等于it指向的元素键值的元素
sword.insert(rew[i]);//屠龙宝刀点击送
ATK=max(ATK,r[i]%a[i]==0?r[i]/a[i]:r[i]/a[i]+1);//处理出至少要砍多少刀
}
printf("%lld\n",ExCRT());
}
return 0;
}
这道题目是非常值得一做的题目,对于理解
如果有不懂的地方或者发现这篇蒟蒻题解哪里有问题欢迎私信D我。(大雾
谢谢管理大大审核^_^