题面:luogu4774
题意:有 n n n条龙,初始生命值 a i a_i ai,恢复能力 p i p_i pi,生命值为负时会恢复,当它生命值恰好为 0 0 0时死亡。初始有 m m m把剑,每把剑有攻击力。每次会选择攻击力 ≤ \le ≤龙初始生命值且攻击力最大的剑,若不存在则选择攻击力最小的剑。当击杀一条龙时,使用的剑会消失,同时会奖励一把新的剑。现在按照 1 → n 1 \to n 1→n的顺序杀龙,固定对每条龙攻击 x x x次,求最少攻击次数。 n ≤ 1 0 5 n \le 10^5 n≤105,所有 p i p_i pi的最小公倍数 ≤ 1 0 12 \le 10^{12} ≤1012, a i ≤ 1 0 12 a_i \le 10^{12} ai≤1012,攻击力 ≤ 1 0 6 \le 10^6 ≤106。保证 p i ≤ a i p_i \le a_i pi≤ai或 p i = 1 p_i = 1 pi=1
题解:首先可以用multiset求出击杀每条龙的剑的攻击值。设杀第 i i i条龙的剑的攻击值为 a t k i atk_i atki。
首先特判 p i = 1 p_i = 1 pi=1的部分分,即求 max ⌈ a i a t k i ⌉ \max \lceil \frac {a_i} {atk_i} \rceil max⌈atkiai⌉
现在即求满足该方程组的最小解 x x x: a t k i ⋅ x ≡ a i m o d    p i atk_i \cdot x \equiv a_i \mod p_i atki⋅x≡aimodpi
因为 p i p_i pi不一定为质数,因此考虑使用扩展中国剩余定理。
但是扩展中国剩余定理的形式是: x ≡ a i m o d    b i x \equiv a_i \mod b_i x≡aimodbi
考虑对原式进行变形。
x x x即为 a t k i ⋅ x − p i ⋅ y = a i atk_i \cdot x - p_i \cdot y = a_i atki⋅x−pi⋅y=ai的解
设该方程求出的特解为 s x sx sx。
那么 x = s x + k ⋅ p i g c d ( a t k i , p i ) x = sx + k \cdot \frac {p_i} {gcd (atk_i, p_i)} x=sx+k⋅gcd(atki,pi)pi, k ∈ Z k \in Z k∈Z
因此 x ≡ s x m o d    p i g c d ( a t k i , p i ) x \equiv sx \mod \frac {p_i} {gcd (atk_i, p_i)} x≡sxmodgcd(atki,pi)pi。即化为了扩展中国剩余定理的标准形式。
乘法会爆long long,注意使用快速乘。
注意特判 a t k i = p i atk_i = p_i atki=pi, a i ̸ = 0 a_i \not= 0 ai̸=0的情况。
#include <bits/stdc++.h>
using namespace std ;
typedef long long ll ;
inline ll read() {
ll x = 0, f = 1; char c; c = getchar() ;
while (c < '0' || c > '9') {if (c == '-') f = -1; c = getchar();}
while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar() ;
return x * f ;
}
inline ll mul (ll x, ll y, ll mod) {
ll res = 0 ;
while (y) {
if (y & 1) res = (res + x) % mod ;
x = (x + x) % mod; y >>= 1;
}
return res ;
}
ll exgcd (ll a, ll b, ll &x, ll &y) {
if (b == 0) {
x = 1; y = 0; return a ;
} else {
ll d = exgcd (b, a % b, y, x) ;
y -= (a / b) * x ;
return d ;
}
}
const int maxn = 1e5 + 10 ;
int n, m ;
bool flagp ;
ll a[maxn], p[maxn], award[maxn], atk[maxn] ;
multiset<ll> s ;
ll b[maxn], c[maxn] ;
bool vis[maxn] ;
inline ll excrt () {
ll x = b[1], M = c[1] ;
for (int i = 2; i <= n; i ++) {
ll tmpx, tmpy ;
ll d = exgcd (M, c[i], tmpx, tmpy) ;
ll f = ((b[i] - x) % c[i] + c[i]) % c[i] ;
if (f % d != 0) return -1 ;
tmpx = mul (tmpx, f / d, c[i] / d) ;
x += tmpx * M ;
M *= c[i] / d; x = (x % M + M) % M ;
}
return x ;
}
inline void solve () {
if (flagp) {
ll ans = 0 ;
for (int i = 1; i <= n; i ++) {
ll cur = a[i] / atk[i] ;
if (a[i] % atk[i]) cur ++ ;
ans = max (ans, cur) ;
}
printf("%lld\n", ans) ;
} else {
for (int i = 1; i <= n; i ++) {
atk[i] %= p[i]; a[i] %= p[i] ;
if (!atk[i] && a[i]) {
printf("-1\n"); return ;
}
ll x, y ;
ll d = exgcd (atk[i], p[i], x, y) ;
if (a[i] % d != 0) {
printf("-1\n"); return ;
}
ll sx = (mul (x, a[i] / d, p[i]) + p[i]) % p[i]; //注意快速乘
b[i] = sx % (p[i] / d) ; c[i] = p[i] / d ;
}
printf("%lld\n", excrt ()) ;
}
}
int main() {
int T = read() ;
while (T --) {
memset (vis, 0, sizeof vis) ;
s.clear() ;
n = read(); m = read() ;
flagp = 1 ;
for (int i = 1; i <= n; i ++) a[i] = read() ;
for (int i = 1; i <= n; i ++) {
p[i] = read() ;
if (p[i] != 1) flagp = 0 ;
}
for (int i = 1; i <= n; i ++) award[i] = read() ;
for (int i = 1; i <= m; i ++) {
int atk = read() ;
s.insert (atk) ;
}
for (int i = 1; i <= n; i ++) {
multiset<ll>::iterator it = s.upper_bound (a[i]) ;
if (it == s.begin()) atk[i] = (*it) ;
else atk[i] = *(-- it) ;
s.erase (it); s.insert (award[i]) ;
}
solve () ;
}
return 0 ;
}