NOI 2018 屠龙勇士(exCRT)
根据题目。对于每一条龙,设其血量为a,回血量为p。选择武器的攻击力为ATK,攻击的最少次数为x。可以得到同余方程:
A
T
K
⋅
x
≡
a
(
m
o
d
p
)
ATK\cdot x\equiv a\pmod p
ATK⋅x≡a(modp)
这个方程也等价于
A
T
K
g
c
d
(
A
T
K
,
p
)
⋅
x
≡
a
g
c
d
(
A
T
K
,
p
)
(
m
o
d
p
g
c
d
(
A
T
K
,
p
)
)
\frac{ATK}{gcd(ATK,p)}\cdot x\equiv \frac{a}{gcd(ATK,p)}\pmod {\frac{p}{gcd(ATK,p)}}
gcd(ATK,p)ATK⋅x≡gcd(ATK,p)a(modgcd(ATK,p)p)
把上面的同余方程转换为下面的好处是,由于模数的变小,让中间数据更不易溢出。
证明:
a
⋅
x
≡
b
(
m
o
d
p
)
a\cdot x\equiv b \pmod p
a⋅x≡b(modp)等价于
a
d
⋅
x
≡
b
d
(
m
o
d
p
d
)
\frac{a}{d}\cdot x\equiv \frac{b}{d}\pmod {\frac{p}{d}}
da⋅x≡db(moddp),其中
d
=
g
c
d
(
a
,
p
)
d=gcd(a,p)
d=gcd(a,p)。
根据扩展欧几里得算法的证明可知,方程
a
⋅
x
≡
b
(
m
o
d
p
)
a\cdot x\equiv b \pmod p
a⋅x≡b(modp)有解当且仅当
g
c
d
(
a
,
p
)
∣
b
gcd(a,p)|b
gcd(a,p)∣b。
若方程
a
⋅
x
≡
b
(
m
o
d
p
)
a\cdot x\equiv b \pmod p
a⋅x≡b(modp)无解,则显然另一个方程也是无解的。
假设方程有解,令
d
=
g
c
d
(
a
,
p
)
d=gcd(a,p)
d=gcd(a,p),有
a
⋅
x
≡
b
(
m
o
d
p
)
⟺
a
⋅
x
−
b
=
y
⋅
p
⟺
a
d
⋅
x
−
b
d
=
y
⋅
p
d
⟺
a
d
⋅
x
≡
b
d
(
m
o
d
p
d
)
\begin{aligned} a\cdot x\equiv b \pmod p &\Longleftrightarrow a\cdot x-b=y\cdot p\\ &\Longleftrightarrow \frac{a}{d}\cdot x-\frac{b}{d}=y\cdot \frac{p}{d}\\ &\Longleftrightarrow \frac{a}{d}\cdot x\equiv \frac{b}{d} \pmod {\frac{p}{d}} \end{aligned}
a⋅x≡b(modp)⟺a⋅x−b=y⋅p⟺da⋅x−db=y⋅dp⟺da⋅x≡db(moddp)
如此一来,整个题目就转换为一个含有n个同余方程的方程组。
{
a
n
s
≡
x
1
(
m
o
d
p
1
)
a
n
s
≡
x
2
(
m
o
d
p
2
)
.
.
.
a
n
s
≡
x
n
(
m
o
d
p
n
)
\left\{\begin{matrix} ans\equiv x_1 \pmod {p_1}\\ ans\equiv x_2 \pmod {p_2}\\ .\\ .\\ .\\ ans\equiv x_n \pmod {p_n} \end{matrix}\right.
⎩⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎧ans≡x1(modp1)ans≡x2(modp2)...ans≡xn(modpn)
通过扩展中国剩余定理就可以轻松求解啦。
需要注意的是,题目有个限制条件:对于每一条龙
a
−
A
T
K
⋅
x
<
0
a-ATK\cdot x<0
a−ATK⋅x<0
所以需要找出所有龙的x的最大值Max。在求得线性同余方程组的解X后,需要满足X>=Max。
因为同余方程的解是一个同余类,其符合解的同余类的代表元为X,我们只需要让 a n s = X + ⌈ M a x − X p ⌉ ⋅ P ans=X+\lceil\frac{Max-X}{p}\rceil\cdot P ans=X+⌈pMax−X⌉⋅P即可。
至于在选择哪把剑🗡的问题,可以考虑用multiset(多重集合)来实现。multiset自带upper_bound函数,支持插入insert,删除erase。
由于中间数据会爆long long,在解同余方程时,选择用慢速乘来替代乘法。
代码如下:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m;
const int max_n=1e5+5;
ll a[max_n];
ll p[max_n];
ll reward[max_n];
ll atk[max_n];
ll Max;
multiset<ll> ms;
void exgcd(ll a,ll b,ll &d,ll &x,ll &y)
{
if(!b){
d=a;x=1;y=0;return ;
}
else{
exgcd(b,a%b,d,y,x);y-=(a/b)*x;return ;
}
}
ll mul(ll a,ll b,ll mod)
{
a%=mod;
ll ans=0;
while(b)
{
if(b&1)ans=(ans+a)%mod;
a=(a+a)%mod;
b>>=1;
}
return (ans%mod+mod)%mod;
}
ll equation(ll a,ll b,ll &p)//a*x=b (mod p)
{
a%=p;b%=p;
ll x,y,d;
exgcd(a,p,d,x,y);
if(b%d)return -1;
x=mul(x,b/d,p/d);
p/=d;
return x;
}
ll solve(void)
{
ll X=0,P=1;
for(int i=1;i<=n;i++)
{
ll t0=a[i]/atk[i]+(a[i]%atk[i]?1:0);//a[i]-x*atk[i]<0
if(t0>Max)Max=t0;
ll b=equation(atk[i],a[i],p[i]);//每个同余方程得解
if(b==-1)return -1;
ll x,y,d;
ll t=((b-X)%p[i]+p[i])%p[i];//EXCRT
exgcd(P,p[i],d,x,y);
if(t%d)return -1;
x=mul(x,t/d,p[i]/d);
X+=x*P;
P*=p[i]/d;
}
if(X>=Max)return X;
else return X+((Max-X)/P+((Max-X)%P?1:0))*P;
}
int main(void)
{
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
int t;
ll t2;
scanf("%d",&t);
while(t--)
{
max=-1;
ms.clear();
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%lld",&a[i]);
for(int i=1;i<=n;i++)
scanf("%lld",&p[i]);
for(int i=1;i<=n;i++)
scanf("%lld",&reward[i]);
for(int i=1;i<=m;i++)
{
scanf("%lld",&t2);
ms.insert(t2);
}
multiset<ll>::iterator it;
for(int i=1;i<=n;i++)
{
it=ms.upper_bound(a[i]);
if(it!=ms.begin())it--;
atk[i]=*it;
ms.erase(it);
ms.insert(reward[i]);
}
printf("%lld\n",solve());
}
return 0;
}