[NOI2018 D2T1]屠龙勇士【扩展欧几里得+EXCRT+平衡树】

题目描述

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

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

游戏开始时玩家拥有 m把攻击力已知的剑,每次面对巨龙时,玩家只能选择一 把剑,当杀死巨龙后这把剑就会消失,但作为奖励,玩家会获得全新的一把剑。 小D 觉得这款游戏十分无聊,但最快通关的玩家可以获得ION2018 的参赛资格, 于是小D 决定写一个笨笨的机器人帮她通关这款游戏,她写的机器人遵循以下规则:

每次面对巨龙时,机器人会选择当前拥有的,攻击力不高于巨龙初始生命值中攻击力最大的一把剑作为武器。如果没有这样的剑,则选择攻击力最低的一把剑作为武器。

机器人面对每条巨龙,它都会使用上一步中选择的剑攻击巨龙固定的 x x 次,使巨龙的生命值减少x×ATK

之后,巨龙会不断使用恢复能力,每次恢复 pi p i 生命值。若在使用恢复能力前或某一次恢复后其生命值为 0 0 ,则巨龙死亡,玩家通过本关。

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

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

输入格式:

从文件dragon.in 中读入数据。
第一行一个整数T ,代表数据组数。
接下来T 组数据,每组数据包含5行。
每组数据的第一行包含两个整数, n n m ,代表巨龙的数量和初始剑的数量;
接下来一行包含 n n 个正整数,第i个数表示第 i i 条巨龙的初始生命值ai
接下来一行包含 n n 个正整数,第i 个数表示第 i i 条巨龙的恢复能力 pi p i
接下来一行包含 n n 个正整数,第i个数表示杀死第 i i 条巨龙后奖励的剑的攻击力;
接下来一行包含 m m 个正整数,表示初始拥有的 m 把剑的攻击力。

输出格式:

输出到文件dragon.out 中。 一共 T T 行。

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

说明

这里写图片描述
特性 1 是指:对于任意的 i i aipi
特性 2 是指: lcm(pi)106 lcm ⁡ ( p i ) ≤ 10 6 即所有 pi p i 的最小公倍数不大于 106 10 6
对于所有的测试点, T5 T ≤ 5 ,所有武器的攻击力 106 ≤ 10 6 ,所有 pi p i 的最小公倍数 1012 ≤ 10 12
保证 T,n,m T , n , m 均为正整数。
【提示】
你所用到的中间结果可能很大,注意保存中间结果的变量类型。


题目分析

由题意可得式子 aiATixmodpi=0 a i − A T i ∗ x mod p i = 0
并化为 axc(modb) a x ≡ c ( mod b ) 的形式

ATixiai(modpi) A T i ∗ x i ≡ a i ( mod p i )
gcd=gcd(a,b) g c d = g c d ( a , b )

上式可由扩展欧几里得最小非负整数解 xi x i
以及通解 xi=xi+bgcdk,kZ x i ′ = x i + b g c d ∗ k , k ∈ Z

题目所求答案 x x 与上述xi关系为 x=xi+bgcdk,kZ x = x i + b g c d ∗ k , k ∈ Z
化为同余方程 xa(modb) x ≡ a ( mod b ) 的形式
xxi(modbgcd) x ≡ x i ( mod b g c d )

也就是说每条巨龙都可以由上述转化得到一个同余方程
最后组成一个同余方程组
直接扩展中国剩余定理 EXCRT求解

不过要特别注意
EXCRT所求出的最小非负整数解并不是我们要的答案
由于每条龙都有固定的剑
所以对于每条龙至少要砍几下是已知的
我们要记录下每条龙至少要砍的次数中的最大值mi
然后EXCRT求出的答案应该是
通解中大于等于mi且最小的解

另外由于本蒟蒻实在太蒟
根本不会什么multiset
于是手写了一个treap,代码略长=_=

还有还有,一定要用龟速乘!!!


#include<iostream>
#include<vector>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cstdio>
using namespace std;
typedef long long lt;

lt read()
{
    lt f=1,x=0;
    char ss=getchar();
    while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
    while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
    return f*x;
}

const lt inf=1e12;
const int maxn=500010;
int t,n,m;
lt ai[maxn],pi[maxn],at[maxn],atk[maxn];
lt a[maxn],b[maxn],mi;

struct node
{
    node* ch[2];
    lt v,r,sum,cnt;
    node(lt v) :v(v) {r=rand();sum=cnt=1;ch[0]=ch[1]=NULL;}
    int cmp(lt x){if(x==v)return -1;return x<v?0:1;}
    void update()
    {
        sum=cnt;
        if(ch[0])sum+=ch[0]->sum;
        if(ch[1])sum+=ch[1]->sum;
    }
};
node* rt;

void rotate(node* &p,int d)
{
    node* k=p->ch[d^1];
    p->ch[d^1]=k->ch[d];
    k->ch[d]=p;
    p->update(); k->update();
    p=k;
}

void ins(node* &p,lt x)
{
    if(p==NULL){ p=new node(x); return;}
    if(x==p->v){ ++p->sum; ++p->cnt; return;}
    int d=p->cmp(x);
    ins(p->ch[d],x);
    if(p->ch[d]->r < p->r)rotate(p,d^1);
    p->update();
}

void del(node* &p,lt x)
{
    if(p==NULL) return;
    if(x==p->v)
    {
        if(p->cnt>1){ p->sum--; p->cnt--; return;}
        if(p->ch[0]==NULL){node *k=p; p=p->ch[1]; delete(k); }
        else if(p->ch[1]==NULL){node *k=p; p=p->ch[0]; delete(k); }
        else
        {
            int dd=p->ch[0]->r < p->ch[1]->r ?1 :0;
            rotate(p,dd); del(p->ch[dd],x);
        }
    }
    else if(x < p->v)del(p->ch[0],x);
    else del(p->ch[1],x);
    if(p!=NULL)p->update();
}

lt pre(node* p,lt x)
{
    if(p==NULL)return -inf;
    if(x==p->v) return p->v;
    else if(x<p->v) return pre(p->ch[0],x);
    else return max(p->v,pre(p->ch[1],x));
}

lt qmin(node* p)
{
    if(p->ch[0]) return qmin(p->ch[0]);
    else return p->v;
}

lt mul(lt a,lt b,lt mod)
{
    lt res=0;
    while(b>0)
    {
        if(b&1) res=(res+a)%mod;
        a=(a+a)%mod;
        b>>=1;
    }
    return res;
}

lt exgcd(lt aa,lt bb,lt &x,lt &y)
{
    if(bb==0){x=1;y=0;return aa;}
    lt gcd=exgcd(bb,aa%bb,x,y);
    lt tp=x;
    x=y; y=tp-aa/bb*y;
    return gcd;
}

lt excrt()
{
    lt M=b[1],ans=a[1],x,y;
    for(int i=2;i<=n;i++)
    {
        lt aa=M,bb=b[i],c=(a[i]-ans%bb+bb)%bb;
        lt gcd=exgcd(aa,bb,x,y),bg=bb/gcd;
        if(c%gcd!=0) return -1; 

        x=mul(x,c/gcd,bg);
        ans+=x*M;
        M*=bg;
        ans=(ans%M+M)%M;
    }
    if(ans>=mi) return ans;//注意答案不一定是最小非负整数解
    else return ans+((mi-ans)/M+((mi-ans)%M ? 1 : 0))*M;
}

int main()
{
    t=read();
    while(t--)
    {
        rt=NULL; mi=0;
        n=read();m=read(); int judge=0;
        for(int i=1;i<=n;++i)ai[i]=read();
        for(int i=1;i<=n;++i)pi[i]=read();
        for(int i=1;i<=n;++i)at[i]=read();
        for(int i=1;i<=m;++i)
        {
            lt x=read();
            ins(rt,x);
        }
        for(int i=1;i<=n;++i)//查找对应每条龙的剑
        {
            atk[i]=pre(rt,ai[i]);
            if(atk[i]==-inf) atk[i]=qmin(rt); 
            del(rt,atk[i]); ins(rt,at[i]);
            mi=max(mi,ai[i]/atk[i]+(ai[i]%atk[i]?1:0));
            //记录每头龙最少要砍的次数的最大值
        }
        for(int i=1;i<=n;++i)//扩欧求ATK*x≡ai(mod pi)
        {
            lt x,y,c=ai[i];
            lt gcd=exgcd(atk[i],pi[i],x,y),bg=pi[i]/gcd;

            if(c%gcd!=0){ judge=1; break;}//无解

            x=(mul(x,c/gcd,bg)+bg)%bg;
            a[i]=x; b[i]=bg;
        }
        if(judge)printf("-1\n");
        else printf("%lld\n",excrt());
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值