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

题目描述

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

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

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

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

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

之后,巨龙会不断使用恢复能力,每次恢复 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

扫描二维码关注公众号,回复: 2800843 查看本文章

说明

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


题目分析

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

A T i x i a i ( mod p i )
g c d = g c d ( a , b )

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

题目所求答案 x 与上述 x i 关系为 x = x i + b g c d k , k Z
化为同余方程 x a ( mod b ) 的形式
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;
}

猜你喜欢

转载自blog.csdn.net/niiick/article/details/81143405