[Noi2018]屠龙勇士(exgcd)

Solution

首先使用可支持 插入 / 删除 / 查找前驱及最小值 的数据结构,求出攻击第 i 条龙的攻击力 a t k i
易得,能杀死第 i 条龙的条件是:

x × a t k i a i

x × a t k i a i ( mod p i )

将第一个式子进行变形:
x a i a t k i

如果存在一个 i 使第二个式子(同余方程)无解,那么原问题无解。
否则解同余方程,得到解,形如:
x r i ( mod q i )

n 个方程放在一起,得到 同余方程组
这便是经典的 扩展中国剩余定理(EXCRT)
具体地,如果解出前 i 1 个方程的解 x R ( mod Q )
那么设前 i 个方程的解为 R + k Q
那么有:
R + k Q r i ( mod q i )

同样是一个线性同余方程。
最后可以得到无解,或者方程的通解:
x = k LCM ( q 1 , q 2 , . . . , q n ) + R

选取最小的大于等于 max a a t k 的解。

Code

注意相乘时可能爆 long long ,要用快速乘。
作为一介场外选手,考场上没用 multiset 丢了 10 分 QwQ

#include <set>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
using namespace std;
typedef long long ll;
typedef multiset<ll>::iterator cyx;
inline int read() {
    int res = 0; bool bo = 0; char c;
    while (((c = getchar()) < '0' || c > '9') && c != '-');
    if (c == '-') bo = 1; else res = c - 48;
    while ((c = getchar()) >= '0' && c <= '9')
        res = (res << 3) + (res << 1) + (c - 48);
    return bo ? ~res + 1 : res;
}
inline ll readll() {
    ll res = 0; bool bo = 0; char c;
    while (((c = getchar()) < '0' || c > '9') && c != '-');
    if (c == '-') bo = 1; else res = c - 48;
    while ((c = getchar()) >= '0' && c <= '9')
        res = (res << 3) + (res << 1) + (c - 48);
    return bo ? ~res + 1 : res;
}
const int N = 1e5 + 5;
int n, m;
ll a[N], p[N], ba[N], at[N], atk[N], sol[N], atle, LCM;
multiset<ll> pyz;
void exgcd(ll a, ll b, ll &x, ll &y) {
    if (!b) return (void) (x = 1, y = 0);
    exgcd(b, a % b, y, x); y -= a / b * x;
}
ll qprod(ll a, ll b, ll MX) {
    ll res = 0;
    while (b) {
        if (b & 1) res = (res + a) % MX;
        a = a * 2 % MX;
        b >>= 1;
    }
    return res;
}
ll solveth(ll atk, ll a, ll p, ll &w) {
    ll g = __gcd(atk, p);
    if (a % g) return -1;
    atk /= g; a /= g; p /= g;
    w = p; ll x, y;
    exgcd(atk, p, x, y);
    x = (x % p + p) % p;
    return qprod(a, x, p);
}
void work() {
    int i;
    n = read(); m = read();
    For (i, 1, n) a[i] = readll();
    For (i, 1, n) p[i] = readll();
    For (i, 1, n) ba[i] = readll();
    For (i, 1, m) at[i] = readll();
    pyz.clear();
    For (i, 1, m) pyz.insert(at[i]);
    For (i, 1, n) {
        cyx it; ll tmp;
        cyx xi = pyz.begin();
        if ((tmp = (*xi)) > a[i])
            pyz.erase(xi), atk[i] = tmp;
        else it = pyz.upper_bound(a[i]),
            atk[i] = (*--it), pyz.erase(it);
        pyz.insert(ba[i]);
    }
    atle = 0;
    For (i, 1, n) {
        sol[i] = solveth(atk[i], a[i], p[i], p[i]);
        if (sol[i] == -1) return (void) (puts("-1"));
        atle = max(atle,
            a[i] % atk[i] ? a[i] / atk[i] + 1 : a[i] / atk[i]);
    }
    LCM = p[1]; ll ans = sol[1];
    For (i, 2, n) {
        ll mov = ((sol[i] - ans) % p[i] + p[i]) % p[i];
        ll g = __gcd(LCM, p[i]), x, y;
        if (mov % g) return (void) (puts("-1"));
        ll tm = LCM / g, Mod = p[i] / g; mov /= g;
        exgcd(tm, Mod, x, y);
        x = (x % Mod + Mod) % Mod;
        ll k = qprod(mov, x, Mod), pl = LCM;
        LCM = LCM / __gcd(LCM, p[i]) * p[i];
        (ans += qprod(k, pl, LCM)) %= LCM;
    }
    ll spa = max(0ll, atle - ans);
    ll k = spa % LCM ? spa / LCM + 1 : spa / LCM;
    cout << ans + k * LCM << endl;
}
int main() {
    //freopen("dragon.in", "r", stdin);
    //freopen("dragon.out", "w", stdout);
    int T = read(); while (T--) work();
    return 0;
}

猜你喜欢

转载自blog.csdn.net/xyz32768/article/details/81169547