-
可以想到第一次屠龙用的刀是确定的,可以列一个同余方程 x ∗ a t k 1 − a 1 = 0 ( m o d p 1 ) x * atk_1 - a_1 = 0 (mod \space p_1) x∗atk1−a1=0(mod p1) ,然后每次用的刀都可以确定,因此预先确定所有的刀,对100%的数据用EXCRT求解同余方程组就可以了。
-
怎么预先确定所有的刀呢?直接写的话可以离散然后用树状数组维护。。。但是有STL
Multiset里的lower_bound 和upper_bound -
使用exgcd求解一般的同余方程 B x = A ( m o d p ) Bx=A (mod \space p) Bx=A(mod p)这里p可能不是质数,B和p可能不互质,上式可化成
B x + P y = A Bx+Py=A Bx+Py=A
从而用exgcd解出x即可。 -
EXCRT的注意事项:数据范围都是long long的,直接乘会爆long long, 要用快速乘
-
这个题的坑:用EXCRT直接求出来的最小正整数解可能不是答案,考虑同余方程之外,题目要求龙的生命值(经过恢复)恰好为0才会死去,因此每条龙都有最小攻击次数。答案应该是通解中满足>=max{最小攻击次数}的值。
只是做到理解具体步骤的程度,还不能完全自己写出来,STL几乎不会用,同余化简要推很久,exgcd,excrt只有有板子的时候才会写,会忘记写快速乘
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <set>
using namespace std;
#define N 100005
typedef long long LL;
int T,n,m;
LL a[N],p[N],btk[N],atk[N],c[N], P;
LL qmul(LL a, LL b, LL P)
{
LL res = 0;
while (b)
{
if (b & 1) res = (res + a) % P;
a = (a + a) % P;
b >>= 1;
}
return res;
}
LL exgcd(LL a, LL b, LL & x, LL & y)
{
if (b == 0) {x = 1;y = 0;return a;}
LL d = exgcd(b, a % b, y, x);
y -= (a / b) * x;
return d;
}
LL excrt()
{
LL M = p[1], ans = a[1], x, y;
for (int i=1;i<=n;i++)
{
LL d = exgcd(M, p[i], x, y), c = ((a[i] - ans) % p[i] + p[i]);
LL tmp = p[i] / d;
if (c % d != 0) return -1;
x = qmul(x, c / d, tmp);
ans += x * M;
M = M * tmp;
ans = (ans % M + M) % M;
}
P = M;
return ans;
}
LL solve()
{
multiset<LL> S;
for (int i=1;i<=m;i++)
S.insert(atk[i]);
multiset <LL>:: iterator it;
LL tmp = 0;
for (int i=1;i<=n;i++){
it = (a[i] < (*S.begin())) ? S.begin():(--S.upper_bound(a[i]));
c[i] = *it;
S.erase(it);
S.insert(btk[i]);
tmp = max(tmp, ((a[i]-1) / c[i] + 1));//上取整
}
//化简同余方程
for (int i=1;i<=n;i++)
{
LL x, y;
LL d = exgcd(c[i], p[i], x, y);
if (a[i] % d != 0) return -1;
x = (x % p[i] + p[i]) % p[i];
a[i] = qmul(x, a[i] / d, p[i]);//! 还是在模p[i]的意义下
p[i] /= d;
}
LL ans = excrt();
if (ans != -1 && ans < tmp)
ans = ans + ((tmp - ans - 1) / P + 1) * P;//上取整
return ans;
}
int main()
{
scanf("%d", &T);
while (T--)
{
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", &btk[i]);
for (int i=1;i<=m;i++) {scanf("%lld", &atk[i]);}
printf("%lld\n", solve());
}
}