zoukankan      html  css  js  c++  java
  • 洛谷 P4774 / loj 2721 [NOI2018] 屠龙勇士 题解【同余】【exgcd】【CRT】

    推导过程存在漏洞+exCRT板子没打熟于是期望得分÷实际得分=∞?

    题目描述

    小 D 最近在网上发现了一款小游戏。游戏的规则如下:

    • 游戏的目标是按照编号 (1sim n​) 顺序杀掉 (n​) 条巨龙,每条巨龙拥有一个初始的生命值 (a_i​)。同时每条巨龙拥有恢复能力,当其使用恢复能力时,它的生命值就会每次增加 (p_i​),直至生命值非负。只有在攻击结束后且当生命值恰好(0​) 时它才会死去。

    • 游戏开始时玩家拥有 (m) 把攻击力已知的剑,每次面对巨龙时,玩家只能选择一把剑,当杀死巨龙后这把剑就会消失,但作为奖励,玩家会获得全新的一把剑。

    小 D 觉得这款游戏十分无聊,但最快通关的玩家可以获得 ION2018 的参赛资格,于是小 D 决定写一个笨笨的机器人帮她通关这款游戏,她写的机器人遵循以下规则:

    • 每次面对巨龙时,机器人会选择当前拥有的,攻击力不高于巨龙初始生命值中攻击力最大的一把剑作为武器。如果没有这样的剑,则选择攻击力最低的一把剑作为武器。
    • 机器人面对每条巨龙,它都会使用上一步中选择的剑攻击巨龙固定的 (x​) 次,使巨龙的生命值减少 (x imes ATK​)
    • 之后,巨龙会不断使用恢复能力,每次恢复 (p_i) 生命值。若在使用恢复能力前或某一次恢复后其生命值为 (0),则巨龙死亡,玩家通过本关。

    那么显然机器人的攻击次数是决定能否最快通关这款游戏的关键。小 D 现在得知了每条巨龙的所有属性,她想考考你,你知道应该将机器人的攻击次数 (x​) 设置为多少,才能用最少的攻击次数通关游戏吗?

    当然如果无论设置成多少都无法通关游戏,输出 -1​ 即可。

    输入格式

    从文件 dragon.in 中读入数据。

    第一行一个整数 (T​),代表数据组数。

    接下来 (T) 组数据,每组数据包含 (5) 行。

    • 每组数据的第一行包含两个整数,(n)(m),代表巨龙的数量和初始剑的数量;
    • 接下来一行包含 (n​) 个正整数,第 (i​) 个数表示第 (i​) 条巨龙的初始生命值 (a_i​)
    • 接下来一行包含 (n) 个正整数,第 (i) 个数表示第 (i) 条巨龙的恢复能力 (p_i)
    • 接下来一行包含 (n) 个正整数,第 (i) 个数表示杀死第 (i) 条巨龙后奖励的剑的攻击力;
    • 接下来一行包含 (m) 个正整数,表示初始拥有的 (m) 把剑的攻击力。

    输出格式

    输出到文件 dragon.out 中。

    一共 (T) 行。

    (i) 行一个整数,表示对于第 (i) 组数据,能够使得机器人通关游戏的最小攻击次数 (x),如果答案不存在,输出 -1

    样例输入

    2
    3 3
    3 5 7
    4 6 10
    7 3 9
    1 9 1000
    3 2
    3 5 6
    4 8 7
    1 1 1
    1 1
    

    样例输出

    59
    -1
    

    样例解释

    第一组数据:

    • 开始时拥有的剑的攻击力为 ({1,9,10}),第 (1) 条龙生命值为 (3),故选择攻击力为 (1)的剑,攻击 (59) 次,造成 (59) 点伤害,此时龙的生命值为 (-56),恢复 (14) 次后生命值恰好为 (0),死亡。
    • 攻击力为 (1) 的剑消失,拾取一把攻击力为 (7) 的剑,此时拥有的剑的攻击力为 ({7,9,10}),第 (2) 条龙生命值为 (5),故选择攻击力为 (7) 的剑,攻击 (59) 次,造成 (413) 点伤害,此时龙的生命值为 (-408),恢复 (68) 次后生命值恰好为 (0),死亡。
    • 此时拥有的剑的攻击力为 ({3,9,10}),第 (3) 条龙生命值为 (7),故选择攻击力为 (3) 的剑,攻击 (59) 次,造成 (177) 点伤害,此时龙的生命值为 (-170),恢复 (17) 次后生命值恰好为 (0),死亡。
    • 没有比 (59) 次更少的通关方法,故答案为 (59)

    第二组数据:

    • 不存在既能杀死第一条龙又能杀死第二条龙的方法,故无法通关,输出-1

    子任务

    测试点编号 $n$ $m$ $p_i$ $a_i$ 攻击力 其他限制
    $1$ $le 10^5$ $=1$ $=1$ $le 10^5$ $=1$
    $2$
    $3$ $le 10^5$
    $4$
    $5$ $le 10^3$ $le 10^3$ $le 10^5$

    特性 $1$、

    特性 $2$

    $6$
    $7$
    $8$ $=1$ $=1$ $le 10^8$ $le 10^8$ $le 10^6$ 特性 $1$
    $9$
    $10$
    $11$
    $12$
    $13$
    $14$ $=10^5$ $=10^5$ $=1$ 无特殊限制
    $15$
    $16$ $le 10^5$ $le 10^5$

    所有 $p_i$ 是质数

    $le 10^{12}$ 特性 $1$
    $17$
    $18$ 无特殊限制
    $19$
    $20$

    特性 (1) 是指:对于任意的 (i)(a_ile p_i)

    特性 (2) 是指:(LCM(p_i)le 10^6) 即所有 (p_i) 的最小公倍数不大于 (10^6)

    对于所有的测试点,(Tle 5),所有武器的攻击力 (le 10^6),所有 (p_i) 的最小公倍数(le 10^{12})

    提示

    你所用到的中间结果可能很大,注意保存中间结果的变量类型。

    题解:

    整道题使用 exCRT 就能弄过去。

    首先我们考虑每一轮用哪把剑砍龙。由于龙的顺序是固定的,所以每次剩下的剑的集合是固定的。但是每一轮都有剑在动态变化,因此我们用一棵平衡树来维护每次需要用的剑。

    此时我们可以预处理砍每条龙用的是哪把剑,令砍第 (i) 条龙用的剑攻击力为 (c_i)

    那么对于每把剑和相应的龙都应该存在一个 (tinmathbb N) ,使得

    [a_i-c_ix+tp_i=0 ]

    把它转化为同余方程的形式即为

    [c_ixequiv a_ipmod{p_i} ]

    同时,我们要保证 (x) 取最小非负整数解时有意义,因此我们先用上面算出来的 (c_i) 分别求出每头龙至少需要砍多少下才会死,然后我们“提前”砍掉那么多。

    即设变量 (xmn) 表示最少要砍的刀数

    [xmn=max_{1le ile n}left{leftlceilfrac{a_i}{c_i} ight ceil ight} ]

    那么对于所有的 (a_i) 都要减去一个 (xmn imes c_i)

    减过之后,对于所有的龙,我们把方程组列出来,有

    [left{ egin{aligned} &c_1xequiv a_1pmod{p_1}\ &c_2xequiv a_2pmod{p_2}\ &cdots\ &c_nxequiv a_npmod{p_n} end{aligned} ight. ]

    变成了一个 CRT 的形式,由于 (p_i) 没有保证互质,所以我们使用 exCRT。

    但是还有一点,我们用 CRT 解决问题时,左侧的 (x) 是没有系数的,我们要考虑怎么样把这边的系数化掉。

    如果用逆元,两边同除 (c_i),那么只在 (p_i) 保证为质数的意义下才能成立,否则逆元无解导致题目答案错误。

    我们可以用不定方程来推不定方程,因为 (c_ixequiv a_i) 有一系列解,而且和 (x) 成线性关系,我们直接从这边转化过来就可以了。

    [c_ixequiv a_ipmod{p_i}\ c_ix+p_it=a_i, tin mathbb Z ]

    这里只有 (x)(t) 是变量,因此只需要把 (x) 的特解和通解求出来,再构造一个形如 (xequiv b_ipmod{d_i}) 的方程就可以了。

    对不定方程 (c_ix+p_it=gcd(c_i,p_i)) 求解,得到 (x=x_0)。等式两边同乘 (frac{a_i}{gcd(c_i,p_i)})(x=frac{x_0a_i}{gcd(c_i,p_i)})。那么 (Delta x=frac {p_i}{gcd(c_i,p_i)})

    由此构造出了新方程

    [xequiv frac{x_0a_i}{gcd(c_i,p_i)}left(mod frac{p_i}{gcd(c_i,p_i)} ight) ]

    原方程组就变成了

    [left{ egin{aligned} &xequiv b_1pmod{d_1}\ &xequiv b_2pmod{d_2}\ &cdots\ &xequiv b_npmod{d_n} end{aligned} ight. ]

    (b_i,d_i) 由上式计算。

    然后套上 exCRT ,最后加上一开始算的 (latex xmn) 即可。

    本来多简单一个题被我公式来公式去整这么复杂。

    Code:

    (平衡树(BST:: 部分)版面较大,multimap 可过)

    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<cmath>
    #define ll long long
    namespace BST
    {
    int ls[200100],rs[200100],key[200100],rdm[200100],sz[200100],pcnt=0,root=0;
    int Newnode(int x)
    {
        ++pcnt;
        key[pcnt]=x;
        rdm[pcnt]=rand();
        ls[pcnt]=rs[pcnt]=0;
        sz[pcnt]=1;
        return pcnt;
    }
    void maintain(int rt)
    {
        sz[rt]=sz[ls[rt]]+sz[rs[rt]]+1;
    }
    int Merge(int a,int b)
    {
        if(!a||!b)
            return a|b;
        if(rdm[a]<rdm[b])
        {
            rs[a]=Merge(rs[a],b);
            maintain(a);
            return a;
        }
        ls[b]=Merge(a,ls[b]);
        maintain(b);
        return b;
    }
    void split(int rt,ll x,int &a,int &b)
    {
        if(!rt)
        {
            a=b=0;
            return;
        }
        if(key[rt]<=x)//不高于
        {
            a=rt;
            split(rs[a],x,rs[a],b);
        }
        else
        {
            b=rt;
            split(ls[b],x,a,ls[b]);
        }
        maintain(rt);
    }
    void Insert(int x)
    {
        int a,b;
        split(root,x,a,b);
        root=Merge(Merge(a,Newnode(x)),b);
    }
    void Delete(int x)
    {
        int a,b,c;
        split(root,x-1,a,b);
        split(b,x,b,c);
        b=Merge(ls[b],rs[b]);
        root=Merge(Merge(a,b),c);
    }
    int getMin(int rt)
    {
        if(ls[rt])
            return getMin(ls[rt]);
        return key[rt];
    }
    int getMax(int rt)
    {
        if(rs[rt])
            return getMax(rs[rt]);
        return key[rt];
    }
    int getsword(ll x)
    {
        int a,b,ans;
        split(root,x,a,b);
        if(!sz[a])
            ans=getMin(b);
        else
            ans=getMax(a);
        root=Merge(a,b);
        return ans;
    }
    }
    
    ll exgcd(ll a,ll b,ll &x,ll &y)
    {
        if(!b)
        {
            x=1,y=0;
            return a;
        }
        ll g=exgcd(b,a%b,y,x);
        y-=a/b*x;
        return g;
    }
    ll qmul(ll x,ll y,ll p)
    {
        ll ans=0,f=1;
        if(x<0)
        {
            x=-x;
            f=-f;
        }
        if(y<0)
        {
            y=-y;
            f=-f;
        }
        while(y)
        {
            if(y&1)
                ans=(ans+x)%p;
            x=(x+x)%p;
            y>>=1;
        }
        return ans*f;
    }
    ll k[100100],a[100100],b[100100],c[100100];//k 指系数
    //c 是奖励的
    int main()
    {
        freopen("dragon.in","r",stdin);
        freopen("dragon.out","w",stdout);
        int T,n,m;
        ll x,y;
        scanf("%d",&T);
        while(T--)
        {
            scanf("%d%d",&n,&m);
            BST::pcnt=BST::root=0;
            for(int i=1;i<=n;++i)
                scanf("%lld",&a[i]);
            for(int i=1;i<=n;++i)
                scanf("%lld",&b[i]);
            for(int i=1;i<=n;++i)
                scanf("%lld",&c[i]);
            for(int i=1;i<=m;++i)
            {
                scanf("%lld",&x);
                BST::Insert(x);
            }
            ll xmn=0;
            for(int i=1;i<=n;++i)
            {
                ll f=BST::getsword(a[i]);
                BST::Delete(f);
                BST::Insert(c[i]);
                c[i]=f;
                xmn=xmn>ceil((double)a[i]/f)?xmn:ceil((double)a[i]/f);
            }
            for(int i=1;i<=n;++i)
                a[i]-=c[i]*xmn;
    
            bool flag=0;
            for(int i=1;i<=n;++i)
            {
                //c[i]*x=a[i](mod b[i])
                ll g=exgcd(c[i],b[i],x,y);
                if(a[i]%g)
                {
                    puts("-1");
                    flag=1;
                    break;
                }
                ll t=b[i]/g;
                //特解 x
                x=(x%t+t)%t;
                x=qmul(x,a[i]/g,t);
                a[i]=x;
                b[i]/=g;//公差
                if(i==1)
                    continue;
                g=exgcd(b[i],b[i-1],x,y);
                if((a[i]-a[i-1])%g)
                {
                    puts("-1");
                    flag=1;
                    break;
                }
                t=b[i-1]/g;
                x=(x%t+t)%t;
                x=qmul(x,(a[i]-a[i-1])/g,t);
                a[i]-=qmul(b[i],x,b[i]/g*b[i-1]);
                b[i]=b[i]/g*b[i-1];
                a[i]=(a[i]%b[i]+b[i])%b[i];
            }
            if(!flag)
                printf("%lld
    ",xmn+((a[n]%b[n])+b[n])%b[n]);
        }
        return 0;
    }
    
  • 相关阅读:
    在静态工具类中需要注入mapper
    springboot整合mybatis之用外置服务器启动项目(二)
    基于java 的websocket的聊天功能,一开始初始化websocket,执行打开连接之后就直接关闭连接了。
    Lwip的相关资料
    轻量级的应用服务器基础框架
    libevent和基于libevent的网络编程
    protobuf的使用
    module graceful-fs for npm
    Filebeat命令参考
    Filebeat原理与简单配置入门
  • 原文地址:https://www.cnblogs.com/wjyyy/p/lg4774.html
Copyright © 2011-2022 走看看