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


    题意

    LOJ #2721. 「NOI2018」屠龙勇士

    题解

    首先假设每条龙都可以打死,每次拿到的剑攻击力为 (ATK)

    这个需要支持每次插入一个数,查找比一个 (le) 数最大的数(或者找到 (>) 一个数的最小数),删除一个数。

    这个东西显然是可以用 std :: multiset<long long> 来处理的(手写权值线段树或者平衡树也行)。

    对于每一条龙我们只能刚好一次秒杀,并且要恰好算血量最后为 (0)(一波带走)。

    然后就转化成求很多个方程:

    [egin{cases} x imes ATK_1 equiv a_1 pmod {p_1} \ ~~~~~~~~~~~~~~~~~~~~~ vdots \ x imes 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() {
    #ifdef zjp_shadow
        freopen ("2721.in", "r", stdin);
        freopen ("2721.out", "w", stdout);
    #endif
    }
    
    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] + mod[i + 1]) % mod[i + 1];
            }
            return rest[n];
        }
    
        void Out() {
            For (i, 1, n) printf ("%lld %lld
    ", 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
    ", tmp);
    
        }
    
    #ifdef zjp_shadow
        cerr << (double) clock() / CLOCKS_PER_SEC << endl;
    #endif
    
        return 0;
    }
    
  • 相关阅读:
    ArcEngine 9.3 学习笔记(十一):地图输出(Printer类,PageLayoutControl控件打印出图,栅格格式文件输出,矢量格式文件输出)
    SpringBoot动态修改Logger的日志级别
    SpringBoot整合SpringDataJpa + QueryDsl以及使用案例
    HPA 自动水平伸缩(基于CPU)
    k8s dashboard:v2.0.5
    PHP debug_backtrace的胡思乱想 豆浆油条
    PHP命名空间(Namespace)初探 豆浆油条
    sphinx全文检索之PHP使用教程 豆浆油条
    sphinx中文分词搜索coreseek windows下安装与基本使用简介 豆浆油条
    PHP闭包(Closure)初探 豆浆油条
  • 原文地址:https://www.cnblogs.com/zjp-shadow/p/9345637.html
Copyright © 2020-2023  润新知