NOI2018D2T1 屠龙勇士
安利一下松松松的OJ: 传送门
Description:
有N条巨龙, 对于每个龙含有\(a_i\)的生命, 你有N + M把砍刀, 其中M把是直接给你的, N把是杀死对应的巨龙才能获得的, 每把砍刀除攻击外没有区别,记为\(b_i\), 每个巨龙一旦血量被砍到0即为死亡, 低于零则每秒回复\(p_i\)的血量, 现在要求你攻击每个巨龙X次, 求X的最小值或判无解.对于剑的选择, 参见具体题目.
Analysis & Solution:
考虑每条龙死亡的条件:
$ A_i + P_i * k = X * b_i\(
\) X * b_i \equiv A_i \pmod{P_i}\(
\) X \equiv A_i * b_i^{-1} \pmod{P_i}$
这样就得到了关于\(X\)的一系列同余方程.
可以用ExCRT求解.
ExCRT:
> 关于求解多组同余组最小整数解的模数非互质情况的算法
[
然后就把两个方程合并成了一个方程, 这样就可以在\(O(nlogn)\) 内求出同余方程组的最小整数解.
然后本题目就可以在\(O(nlogn)\)内求出解, 注意取模时候用快速乘或int128, 然而€€£并不能用int128.
Code:
#include<bits/stdc++.h>
#define rep(i, a, b) for(int i = (a), i##_end_ = (b); i <= i##_end_; ++i)
#define drep(i, a, b) for(int i = (a), i##_end_ = (b); i >= i##_end_; --i)
#define clar(a, b) memset((a), (b), sizeof(a))
using namespace std;
typedef long long LL;
typedef long double LD;
LL read() {
LL x = 0, flag = 1;
char ch = getchar();
while(!isdigit(ch)) {
if(ch == '-') flag *= -1;
ch = getchar();
}
while(isdigit(ch)) {
x = (x << 3) + (x << 1) + (ch - 48);
ch = getchar();
}
return x * flag;
}
void write(LL a) {
if(a >= 10) write(a / 10);
putchar(a % 10 + '0');
}
#define Maxn 100009
multiset <LL> Set;
LL a[Maxn], p[Maxn], l[Maxn], z[Maxn], sword[Maxn], csf[Maxn][3];
int n, m;
void refresh_mem() {
clar(a, 0), clar(p, 0), clar(l, 0), clar(z, 0), clar(csf, 0);
clar(sword, 0);
}
LL qmul(LL x, LL y, LL mod) {
LL ret = 0;
while(y) {
if(y & 1) ret = (ret + x) % mod;
(x <<= 1) %= mod, y >>= 1;
}
return ret;
}
LL Extend_Euclid(LL a,LL b,LL &x,LL &y){
if(!b){
x = 1, y = 0;
return a;
}
LL d = Extend_Euclid(b, a % b, x, y), t = x;
x = y; y = t - a / b * y;
return d;
}
LL gcd(LL a, LL b) { return b ? gcd(b, a % b) : a; }
LL inv(LL a, LL b){
LL x, y; LL t = Extend_Euclid(a, b, x, y);
if(t != 1) return -1;
return (x % b + b) % b;
}
int merge(LL &a1, LL &n1, LL a2, LL n2) {
LL d = gcd(n1, n2), c = a2 - a1, tmp1, tmp2;
if(c % d != 0) return 0;
c = (c % n2 + n2) % n2 / d;
n1 /= d, n2 /= d;
c = ((__int128)c) * inv(n1, n2) % n2;
tmp2 = n1 * n2 * d;
c = ((((__int128)c) * n1 % tmp2 * d % tmp2) + a1) % tmp2;
tmp1 = (c + tmp2) % tmp2;
a1 = tmp1, n1 = tmp2;
return 1;
}
LL ExCRT(int cnt_equ) {
LL a1 = csf[1][1], n1 = csf[1][2]; // remainder & mod
rep(i, 2, cnt_equ) if(!merge(a1, n1, csf[i][1], csf[i][2])) return -1;
return (a1 % n1 + n1) % n1;
}
void solve() { cout << ExCRT(n) << endl; }
int main() {
int T = read();
while(T--) {
refresh_mem();
LL LM = -1;
n = read(), m = read();
Set.clear();
rep(i, 1, n) a[i] = read();
rep(i, 1, n) p[i] = read(), LM = max(LM, p[i]);
rep(i, 1, n) z[i] = read();
rep(i, 1, m) l[i] = read(), Set.insert(l[i]);
Set.insert((LL)1e13);
rep(i, 1, n) {
set<LL> :: iterator tmp = Set.upper_bound(a[i]);
if(tmp != Set.begin()) --tmp;
sword[i] = *tmp; Set.erase(tmp); Set.insert(z[i]);
}
if(LM == 1) {
LL ans = 0;
rep(i, 1, n) {
LL tmp = a[i] / sword[i];
if(sword[i] * tmp < a[i]) ++tmp;
ans = max(ans, tmp);
}
cout << ans << endl;
continue;
}
int flag = 0;
rep(i, 1, n) {
LL z = gcd(gcd(a[i], p[i]), sword[i]);
a[i] /= z; p[i] /= z; sword[i] /= z;
z = inv(sword[i], p[i]);
if(z == -1) {
flag = 1;
break;
}
csf[i][1] = qmul(a[i], z, p[i]);
csf[i][2] = p[i];
}
if(!flag) solve(); else puts("-1");
}
return 0;
}
// -O2