1. 程式人生 > >LOJ #2721. 「NOI2018」屠龍勇士(set + exgcd)

LOJ #2721. 「NOI2018」屠龍勇士(set + exgcd)

就是 emc ifd etc ins const sta class lse

題意

LOJ #2721. 「NOI2018」屠龍勇士

題解

首先假設每條龍都可以打死,每次拿到的劍攻擊力為 \(ATK\)

這個需要支持每次插入一個數,查找比一個 \(\le\) 數最大的數(或者找到 \(>\) 一個數的最小數),刪除一個數。

這個東西顯然是可以用 std :: multiset<long long> 來處理的(手寫權值線段樹或者平衡樹也行)。

對於每一條龍我們只能剛好一次秒殺,並且要恰好算血量最後為 \(0\)(一波帶走)。

然後就轉化成求很多個方程:
\[ \begin{cases} x \times ATK_1 \equiv a_1 \pmod {p_1} \~~~~~~~~~~~~~~~~~~~~~ \vdots \x \times ATK_n \equiv a_n \pmod {p_n} \\end{cases} \]


求最小正整數解 \(x\) 滿足這些所有方程。

如果把 \(ATK_i\) 除到右邊去,也就是
\[ x \equiv \frac{a_i}{ATK_i} \pmod {p_i} \]
就轉化成求模線性方程組的最小整數解了,可以參考 我的數論總結 ,但是那個板子有個地方需要 慢速乘

這個需要用 \(exgcd\) 計算逆元,如果沒有逆元那麽對於這個方程是無解的。

但是這個有點特殊情況,也就是 \(\gcd(ATK_i, a_i, p_i) > 1\) 的時候,需要約去 \(gcd\)

比如 \(2x \equiv 8 \pmod {36}\) 的時候,顯然 \(x = 4\) 是其中的一個解,但 \(2\)

對於 \(36\) 沒有逆元。

但將方程轉化後 \(x \equiv 4 \pmod {18}\) 就是等價於原來方程的另一個可行方程。

然後如果這個方程仍然沒有逆元的話就是真的無解了。

如果合並方程組中無解那也是無解。

還有一個地方對於 \(p_i = 1\) 的情況,解出來是 \(x \equiv 0 \pmod {1}\)

這個需要給答案有一個下界 \(a_i\) ,最後要一直加上 \(lcm\) 使得它不小於這個下界。

然後各種地方註意會爆 long long ,慢速乘就好了。(掛了 \(15pts\) 。。)

代碼

#include <bits/stdc++.h>

#define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i)
#define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i)
#define Set(a, v) memset(a, v, sizeof(a))
#define Cpy(a, b) memcpy(a, b, sizeof(a))
#define debug(x) cout << #x << ": " << x << endl
#define DEBUG(...) fprintf(stderr, __VA_ARGS__)

using namespace std;

typedef long long ll;

inline ll read() {
    ll x = 0, fh = 1; char ch = getchar();
    for (; !isdigit(ch); ch = getchar()) if (ch == '-') fh = -1;
    for (; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ 48);
    return x * fh;
}

void File() {
    freopen ("dragon.in", "r", stdin);
    freopen ("dragon.out", "w", stdout);
}

void Exgcd(ll a, ll b, ll &x, ll &y) {
    if (!b) x = 1, y = 0;
    else Exgcd(b, a % b, y, x), y -= a / b * x;
}

inline ll Mult(ll x, ll y, ll Mod) {
    ll res = 0; y = (y % Mod + Mod) % Mod;
    for (; y; y >>= 1, (x += x) %= Mod)
        if (y & 1) (res += x) %= Mod;
    return res;
}

const int N = 1e5 + 1e3;
namespace Equations {

    int n; ll mod[N], rest[N];
    ll Solve() {
        For (i, 1, n - 1) {
            ll a = mod[i], b = mod[i + 1], c = rest[i + 1] - rest[i], gcd = __gcd(a, b), k1, k2;
            if (c % gcd) return - 1;
            a /= gcd; b /= gcd; c /= gcd;
            Exgcd(a, b, k1, k2);

            k1 = Mult(k1, c, b);
            mod[i + 1] = mod[i] / __gcd(mod[i], mod[i + 1]) * mod[i + 1] ;
            rest[i + 1] = (mod[i] * k1 % mod[i + 1] + rest[i]) % mod[i + 1];
        }
        return rest[n];
    }

    void Out() {
        For (i, 1, n) printf ("%lld %lld\n", mod[i], rest[i]);
    }

};

multiset<ll> S;
inline ll Find(ll x) {
    multiset<ll> :: iterator it = S.upper_bound(x);
    if (it != S.begin()) -- it;
    ll res = *it; S.erase(it); return res;
}

int n, m; ll a[N], p[N], atk[N], award[N];

inline ll Get_Inv(ll bas, ll Mod) {
    if (__gcd(bas, Mod) != 1) return -1;
    static ll x, y;
    Exgcd (bas, Mod, x, y);
    return (x % Mod + Mod) % Mod;
}

int main () {
    File();

    int cases = read();

    while (cases --) {
        n = read(); m = read();

        For (i, 1, n) a[i] = read();
        For (i, 1, n) p[i] = read();

        For (i, 1, n)
            award[i] = read();

        For (i, 1, m) { ll x = read(); S.insert(x); }

        For (i, 1, n)
            atk[i] = Find(a[i]), S.insert(award[i]);
        S.clear();

        Equations :: n = n;
        bool flag = true;

        ll ans = 0, lcm = 1;
        For (i, 1, n) {
            ll gcd = __gcd(__gcd(atk[i], p[i]), a[i]);
            a[i] /= gcd; atk[i] /= gcd; p[i] /= gcd;

            if (p[i] == 1) {
                ans = max(ans, a[i] / atk[i] + (a[i] % atk[i] ? 1 : 0));
            }

            ll tmp = Get_Inv(atk[i], p[i]);
            if (tmp == -1) { flag = false; break; }

            Equations :: mod[i] = p[i];
            Equations :: rest[i] = Mult(a[i] % p[i], tmp % p[i], p[i]);
            lcm = lcm / __gcd(lcm, p[i]) * p[i];
        }

        if (!flag) { puts("-1"); continue ; }

        ll tmp = Equations :: Solve();
        if (tmp == -1) { puts("-1"); continue ; }

        if (tmp < ans) {
            ll gap = (ans - tmp) / lcm;
            tmp += gap * lcm;
            while (tmp < ans) tmp += lcm;
            while (tmp - lcm > ans) tmp -= lcm;
        }

        printf ("%lld\n", tmp);

    }

#ifdef zjp_shadow
    cerr << (double) clock() / CLOCKS_PER_SEC << endl;
#endif

    return 0;
}

LOJ #2721. 「NOI2018」屠龍勇士(set + exgcd)