P4774 [NOI2018]屠龙勇士(exCRT)

P4774 [NOI2018]屠龙勇士(exCRT)

题目链接:传送门

思路

​     首先根据题意我们可以算出来对于每一条龙使用的武器攻击力(这个使用set和upper_bound成员函数很容易实现)。

​     现在我们价假设第 i ​ i​ i 条龙的生命值为 h [ i ] ​ h[i]​ h[i],恢复能力为 p [ i ] ​ p[i]​ p[i],此时使用的武器攻击力为 g [ i ] ​ g[i]​ g[i], 那么我们攻击次数 x ​ x​ x 需要满足这样的方程式 ( h [ i ] − x ∗ g [ i ] ) % p [ i ] = 0 ​ (h[i]-x*g[i])\%p[i]=0​ (h[i]xg[i])%p[i]=0 ,且 h [ i ] − x ∗ g [ i ] ≤ 0 ​ h[i]-x*g[i]\le 0​ h[i]xg[i]0

​     现在我们有n个方程我们根据所有的第二个限制条件求出来 x x x的最小值。接下来就是对所有的第一个方程求出x的值即可。很容易看出第一个方式即 x ∗ g [ i ] = h [ i ]     (    m o d    p [ i ] ) x*g[i]=h[i] ~~~(~~mod~~p[i]) xg[i]=h[i]   (  mod  p[i])
    我们把等式两边全除以 g [ i ] , h [ i ] , p [ i ] g[i],h[i],p[i] g[i],h[i],p[i]的gcd,得到方程 x ∗ g [ i ] / g c d = h [ i ] / g c d    (   m o d   p [ i ] / g c d ) x*g[i]/gcd=h[i]/gcd~~(~mod~p[i]/gcd) xg[i]/gcd=h[i]/gcd  ( mod p[i]/gcd) 此时我们将等式两边同时乘以 g [ i ] / g c d g[i]/gcd g[i]/gcd关于 p [ i ] / g c d p[i]/gcd p[i]/gcd的逆元,这样方程就是一个一次同余方程了。 如果 g [ i ] / g c d g[i]/gcd g[i]/gcd关于 p [ i ] / g c d p[i]/gcd p[i]/gcd的逆元不存在,那么证明两个数不互质(此时 g [ i ] / g c d g[i]/gcd g[i]/gcd p [ i ] / g c d p[i]/gcd p[i]/gcd 互质),不等式无解,直接输出-1。否则最终得到n个一次同余方程,那么用exCRT解出 a , m a,m a,m即可,因为 a a a是模 m m m的,所以如果a小于x的最小值的话,就让a增加m的倍数即可。

    但是要注意的是,这题数据都在long long 的范围,中间相乘可能会爆long long ,那么此时要有龟速乘法。

本来想做一道CRT合并的题,谁知道只是个exCRT的,第一次乘法爆long long了导致只拿了85分,
爆long long 这可能就是有几次做CRT的题思路是对的,但总是wa的原因吧。

代码

#include
#define mset(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long ll;
const int N=1e5+100;
const int inf=0x3f3f3f3f;
ll h[N],p[N],g[N],s[N];
multiset<ll> mmp;
ll qpow(ll a,ll b,ll m)
{
    ll ans=1;
    while(b)
    {
        if(b&1) ans=(ans*a)%m;
        a=a*a%m;
        b>>=1;
    }
    return ans;
}
ll mul(ll a,ll b,ll m)
{
    ll ans=0,det=1;
    if( a < 0){ det*=-1; a=-a; }
    if(b < 0){ det*=-1; b=-b; }
    while(a)
    {
        if(a&1) ans=(ans+b)%m;
        b=b*2%m;
        a/=2;
    }
    return (ans*det%m+m)%m;
}
long long extend_gcd(long long a,long long b,long long &x,long long &y)
{
    //函数结束后满足 x*a+y*b=gcd
    if(a==0&&b==0) return -1;
    //无最大公约数
    if(b==0)
    {
        x=1;
        y=0;
        return a;
    }
    long long d=extend_gcd(b,a%b,y,x);
    y-=a/b*x;
    return d;
}
//*********求逆元素*******************
//ax = 1(mod n)
long long mod_reverse(long long a,long long n)
{
    long long x,y;
    long long d=extend_gcd(a,n,x,y);
    if(d==1) return (x%n+n)%n;
    else return -1;
}
bool solve(ll &m0,ll &a0,ll m,ll a)
{
    long long y,x;
    ll g = extend_gcd(m0,m,x,y);//得到的x是m0/g关于m1/g的逆元
    if( abs(a - a0)%g )return false;
//    x *= (a - a0)/g;
//    x %= m/g;
    x=mul(x,(a-a0)/g,m/g);//**
    ll d=m0;
//    a0 = (x*m0 + a0);
    m0 *= m/g;  //合并方程后的模数是增大的
    a0=(mul(x,d,m0)+a0)%m0;//**
//    a0 %= m0;
    if( a0 < 0 )a0 += m0;  //a0也是慢慢增大的
    return true;
}

ll a[N],m[N];//模数为m余数为a
bool MLES(ll &m0,ll &a0,ll a[],ll m[],ll n) //解为  X = a0 + m0 * k
{
    bool flag = true;
    m0 = 1;
    a0 = 0;
    for(ll i = 1; i <=n; i++)
        if( !solve(m0,a0,m[i],a[i]) )
        {
            flag = false;
            break;
        }
    return flag;
}

int main()
{
    ios::sync_with_stdio(false);cin.tie(0);
    ll t;
    cin>>t;
    while(t--)
    {
        mmp.clear();
        ll n,_m;
        cin>>n>>_m;
        for(ll i=1; i<=n; ++i) cin>>h[i];
        for(ll i=1; i<=n; ++i) cin>>p[i];
        for(ll i=1; i<=n; ++i) cin>>s[i];
        for(ll i=1; i<=_m; ++i)
        {
            ll v;
            cin>>v;
            mmp.insert(v);
        }
        for(ll i=1; i<=n; ++i)
        {
            ll v;
            auto it=mmp.upper_bound(h[i]);
            if(it!=mmp.begin())
            {
                v=*(--it);
                mmp.erase(it);
            }
            else
            {
                v=*it;
                mmp.erase(it);
            }
            mmp.insert(s[i]);
            g[i]=v;
        }
        ll minx=-1;
        ll ok=true;
        for(ll i=1; i<=n&&ok; ++i)
        {
            ll gcd=__gcd(h[i],__gcd(p[i],g[i]));
            h[i]/=gcd;
            p[i]/=gcd;
            g[i]/=gcd;
            if(!g[i] && !h[i]) continue;
            if(!g[i]&&h[i]) {
                ok=false;
                break;
            }
            ll v=(h[i]+g[i]-1)/g[i];
            minx=max(minx,v);
            m[i]=p[i];
            v=mod_reverse(g[i],p[i]);
            if(v==-1)
            {
                ok=false;
                break;
            }
            else
            {
                a[i]=mul(h[i],v,m[i]);//h[i]*v)%m[i];
            }
        }
        if(!ok)
        {
            cout<<"-1"<<endl;
            continue;
        }
        ll aa=1,mm=1;
        if(!MLES(mm,aa,a,m,n))
        {
            cout<<-1<<endl;
        }

        else{
            if(aa>=minx){
                cout<<aa<<endl;
            }
            else{
                ll v=((minx-aa)+mm-1)/mm;
                cout<<aa+v*mm<<endl;
            }
        }
    }
    return 0;
}

你可能感兴趣的:(#,扩展欧几里得/中国剩余定理)