洛谷4774 BZOJ5418 NOI2018 屠龙勇士 扩展中国剩余定理 multiset

版权声明:本文为博主原创文章,可以转载但是必须声明版权。 https://blog.csdn.net/forever_shi/article/details/88892374

题目链接

题意比较麻烦,感觉我来说也简单不了多少,就不说了,直接看原题吧。

题解:
一道题解咕了大半年的题,现在终于来补锅了。当时网上同步赛的时候我是真的菜啊,那时候就写了一些特殊性质的点,甚至连这是一堆同余方程之后求个最小解都没看出来。

感觉这个题前面部分出的没什么意思,就强行拿个multiset来模拟算出每一次该用的是哪一把剑。确定了这个之后,我们来考虑这个回血的过程,我们发现和取模有点像。于是我们就会列出一堆同余方程,形如 a t k i x = a i ( m o d   p i ) atk_i*x=a_i(mod\ p_i) ,然后我们要求出一个满足所有方程的最小 x x 。这个东西没有什么互质的性质,那么我们就去看看能不能用扩展中国剩余定理。但是我们发现另一个问题是,不能直接把 a t k i atk_i 除过去,因为可能 a t k i atk_i p i p_i 不互质,没有逆元。那么我们的做法是先求出他们的gcd,然后这个式子整体除掉这个gcd,这样就能求逆元了。

主要思路和做法就是这个样子,但是问题在于,这个题比较烦人,他还有很多的细节。首先,可能本来的血量 a i a_i p i p_i 大,这样你求出来的最小值是不对的,因为你是找到了 a i a_i p i p_i 之后的值要多少次,我们还要把它先弄到一个小于 p i p_i 的值。但是对于这个问题,这个题有一个比较有意思的地方,他的所有测试点要么满足 a i < = p i a_i<=p_i ,要么满足 p i = 1 p_i=1 。那么我们就特判一下, p i = 1 p_i=1 很好做,就是只要打到血量小于 0 0 就可以了。于是求法就是每一个的血量除以每次的攻击力上取整,然后取个max就完了。

还要各种奇怪的特例,我有点不想分析了,感觉这个题主要是挺烦的。想看的看我代码吧。

代码:

#include <bits/stdc++.h>
using namespace std;

int T,n,m,pd,ji;
long long a[100010],p[100010],k[100010],b[100010];
multiset <long long> s;
inline long long gcd(long long x,long long y)
{
    return y?gcd(y,x%y):x;
}
inline void exgcd(long long a,long long b,long long &x,long long &y)
{
    if(!b)
    {
        x=1;
        y=0;
    }
    else
    {
        exgcd(b,a%b,y,x);
        y-=a/b*x;
    }
}
inline long long mul(long long x,long long y,long long mod)
{
    y%=mod;
    if(y<0)
    y+=mod;
    long long res=0;
    while(y)
    {
        if(y&1)
        res=(res+x)%mod;
        x=(x<<1)%mod;
        y>>=1;
    }
    return res;
}
inline long long inv(long long a,long long b)
{
    long long x,y;
    a%=b;
    exgcd(a,b,x,y);
    x=(x%b+b)%b;
    if(!x)
    x+=b;
    return x;
}
int main()
{
    scanf("%d",&T);
    multiset<long long>::iterator it;
    while(T--)
    {
        s.clear();
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;++i)
        scanf("%lld",&a[i]);
        for(int i=1;i<=n;++i)
        scanf("%lld",&p[i]);
        for(int i=1;i<=n;++i)
        scanf("%lld",&b[i]);
        for(int i=1;i<=m;++i)
        {
            long long x;
            scanf("%lld",&x);
            s.insert(x);
        }
        pd=0;
        ji=0;
        for(int i=1;i<=n;++i)
        {
            it=s.upper_bound(a[i]);
            if(it!=s.begin())
            --it;
            k[i]=*it;
            s.erase(it);
            s.insert(b[i]);
        }
    	for(int i=1;i<=n;++i)
    	{
    		if(p[i]!=1)
    		ji=1;
        }
        if(ji==0)
        {
            long long res=0;
            for(int i=1;i<=n;++i)
            res=max(res,(a[i]+k[i]-1)/k[i]);
            printf("%lld\n",res);
            continue;
        }
        ji=0;
        for(int i=1;i<=n;++i)
    	{
    		if(p[i]!=a[i])
    		ji=1;
        }
        if(ji==0)
        {
            long long ans=1;
            for(int i=1;i<=n;++i)
            {
                if(p[i]%k[i])
                {
                    pd=1;
                    printf("-1\n");
                    break;
                }
                long long qwq=(p[i]+k[i-1])/k[i];
                ans=ans/gcd(ans,qwq)*qwq;
            }
            if(pd==0)
            printf("%lld\n",ans);
            continue;
        }
        for(int i=1;i<=n;++i)
        {
            k[i]%=p[i];
            if(!k[i]&&a[i]==p[i])
            {	
                k[i]=1;
                p[i]=1;
                a[i]=0;			
            }
            else if(!k[i])
            {
                printf("-1\n");
                pd=1;
                break;
            }
        }
        if(pd==1)
        continue;
        for(int i=1;i<=n;++i)
        {
            long long g=gcd(k[i],p[i]);
            if(a[i]%g)
            {
                printf("-1\n");
                pd=1;
                break;
            }
            if(pd==1)
            break;
            long long x,y;			
            exgcd(k[i],p[i],x,y);			
            p[i]/=g;					
            x=(x%p[i]+p[i])%p[i];			
            a[i]=mul(a[i]/g,x,p[i]);			
        }
        if(pd==1)
        continue;
        for(int i=1;i<=n-1;++i)
        {
            long long g=gcd(p[i],p[i+1]); 			
            if((a[i+1]-a[i])%g)
            {
                printf("-1\n");
                pd=1;
                break;
            }
            if(pd==1)
            break;
            long long ji=mul(inv(p[i]/g,p[i+1]/g),(a[i+1]-a[i])/g,p[i+1]/g);	
            a[i+1]=(mul(ji,p[i],p[i+1]/g*p[i])+a[i])%(p[i+1]/g*p[i]);
            p[i+1]=p[i+1]/g*p[i];
        }
        if(pd==0)
        printf("%lld\n",a[n]);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/forever_shi/article/details/88892374
今日推荐