Luogu4774 NOI2018 屠龙勇士 ExCRT

传送门


原来NOI也会出裸题啊……

用multiset求出对付每一个BOSS使用的武器威力\(ATK_i\),可以得到\(m\)个式子\(ATK_ix \equiv a_i \mod p_i\)

看起来可以直接魔改式子了……

等一下!如果\(a_i > p_i\),\(ATK_ix<a_i\)没把BOSS打死怎么办QAQ

看数据范围,没有特性1(\(a_i \leq p_i\))的点似乎\(p_i=1\)?那不只要保证攻击次数能够把所有BOSS血量打到\(\leq 0\)就行了,,,于是这个顾虑就消除了(虽然要写数据分治)

考虑上面得到的式子,很像ExCRT,但ExCRT的式子都长\(x \equiv b_i \mod p_i\),这里的式子不长这样。于是考虑改式子。

如果\(gcd(ATK_i , p_i) \not\mid a_i\),显然原式无解。

当\(gcd(ATK_i , p_i) \mid a_i\)时,求出\(ATK_ix + p_iy = gcd(ATK_i,p_i)\)的一组解\((x_1,y_1)\),那么\(ATK_ix + p_iy = a_i\)的一组解就是\((\frac{x_1a_i}{gcd(ATK_i,p_i)} , \frac{y_1a_i}{gcd(ATK_i,p_i)})\)。

那么\(ATK_ix \equiv a_i \mod p_i\)的通解就是\(x \equiv \frac{x_1a_i}{gcd(ATK_i,p_i)} \mod \frac{p_i}{gcd(ATK_i,p_i)}\)。

这个式子长得跟ExCRT的式子相同了,直接套板子即可。

值得注意的是,因为模数可能超过int,导致可能出现乘法爆long long。解决方案是log龟速乘/long double型快速乘/__int128

// luogu-judger-enable-o2
#include<bits/stdc++.h>
#define ll long long
//This code is written by Itst
using namespace std;

inline ll read(){
    ll a = 0;
    char c = getchar();
    while(!isdigit(c))
        c = getchar();
    while(isdigit(c)){
        a = (a << 3) + (a << 1) + (c ^ '0');
        c = getchar();
    }
    return a;
}

inline void exgcd(ll a , ll b , ll &d , ll &x , ll &y){
    !b ? d = a , x = 1 , y = 0 : (exgcd(b , a % b , d , y , x) , y -= a / b * x);
}

inline ll max(ll a , ll b){
    return a > b ? a : b;
}

inline ll slow_times(ll a , ll b , ll MOD){
    ll ans = 0;
    while(b){
        if(b & 1)
            ans = (ans + a) % MOD;
        a = (a << 1) % MOD;
        b >>= 1;
    }
    return ans;
}

const int MAXN = 1e5 + 10;
multiset < ll > sword;
multiset < ll > :: iterator it;
int atk[MAXN] , ATK[MAXN] , N , M;
ll ans , P , gcd , x , y , a[MAXN] , m[MAXN] , hp[MAXN] , re[MAXN];

signed main(){
#ifndef ONLINE_JUDGE
    freopen("in" , "r" , stdin);
    //freopen("out" , "w" , stdout);
#endif
    for(int T = read() ; T ; --T){
        N = read();
        M = read();
        bool special = 1;

        for(int i = 1 ; i <= N ; ++i)
            hp[i] = read();
        for(int i = 1 ; i <= N ; ++i)
            special &= (re[i] = read()) == 1;
        for(int i = 1 ; i <= N ; ++i)
            atk[i] = read();
        sword.clear();
        for(int i = 1 ; i <= M ; ++i)
            sword.insert(read());

        for(int i = 1 ; i <= N ; ++i){
            it = sword.upper_bound(hp[i]);
            if(it != sword.begin())
                --it;
            ATK[i] = *it;
            sword.erase(it);
            sword.insert(atk[i]);
        }

        ans = 0;
        
        if(special){
            for(int i = 1 ; i <= N ; ++i)
                ans = max(ans , hp[i] / ATK[i] + (hp[i] % ATK[i] ? 1 : 0));
            cout << ans << '\n';
            continue;
        }
        
        bool f = P = 1;

        for(int i = 1 ; f && i <= N ; ++i){
            exgcd(ATK[i] , re[i] , gcd , x , y);
            if(hp[i] % gcd)
                f = 0;
            x = x % re[i];
            if(x < 0)
                x += re[i];
            m[i] = re[i] / gcd;
            a[i] = slow_times(x , hp[i] / gcd , m[i]);
        }
        
        for(int i = 1 ; f && i <= N ; ++i){
            ll pre = (a[i] - ans % m[i] + m[i]) % m[i];
            exgcd(P , m[i] , gcd , x , y);
            if(pre % gcd)
                f = 0;
            x = x % m[i];
            if(x < 0)
                x += m[i];
            ans = (ans + slow_times(slow_times(x , P , P / gcd * m[i]) , pre / gcd , P / gcd * m[i])) % (P / gcd * m[i]);
            P = P / gcd * m[i];
        }
        
        cout << (f ? ans : -1) << '\n';
    }
    return 0;
}
上一篇:【Spark】Spark-shell案例——standAlone模式下读取HDFS上存放的文件


下一篇:Spark之standalone模式