zoukankan      html  css  js  c++  java
  • 洛谷P4774 BZOJ5418 LOJ2721 [NOI2018]屠龙勇士(扩展中国剩余定理)

    题目链接:

    洛谷

    BZOJ

    LOJ

    题目大意:这么长的题面,就饶了我吧emmm


    这题第一眼看上去没法列出同余方程组。为什么?好像不知道用哪把剑杀哪条龙……

    仔细一看,要按顺序杀龙,所以获得的剑出现的顺序也是固定的。

    那么如果能把所有龙杀死,就能模拟出哪把剑杀那条龙了。

    (以下设所有除 $n,m$ 外的数的最大值为 $v$)

    $O(nm)$?

    不,发现这里用剑的限制实际上是给出一个上界,来用lower_bound的。

    插入也不要太暴力。我们想到什么?手写平衡树multiset!

    这一部分复杂度是 $O(nlog m)$。


    接下来就成了一个同余方程组:$atk_ixequiv hp_i(operatorname{mod} rec_i)$。

    ($atk_i$ 表示攻击第 $i$ 条龙的攻击力,$hp_i$ 表示第 $i$ 条龙的血量,$rec_i$ 表示第 $i$ 条龙的恢复能力)

    但是好像没那么简单!有一些神奇的情况……要特判!

    在考场上的话我肯定想不到特判任何东西

    首先大家应该都知道,$xequiv a_i(operatorname{mod} b_i)$ 等价于 $xequiv a_i operatorname{mod} b_i(operatorname{mod} b_i)$。

    但是在这题中不行!你总不能把一头龙的血量凭空减少吧!

    所以 $hp_i>rec_i$ 时(题目描述中的特性1不满足时)怎么办?

    我们发现不满足特性1的点都满足 $rec_i=1$……也就是只要把他的血量打到小于0就赢了!

    此时答案是 $maxlimits_{1le ile n}(lceilfrac{hp_i}{atk_i} ceil)$。

    另外要注意 $xgemax(lceildfrac{hp_i}{atk_i} ceil)$。

    至此所有特判结束。


    我们一般的EXCRT同余方程组的形式是 $xequiv a_i(operatorname{mod} b_i)$。

    但是这里有讨厌的 $atk_i$!更讨厌的是可能没有逆元,不能直接除过去!

    我们来看看如何变形:

    $atk_ixequiv hp_i(operatorname{mod} rec_i)$

    $atk_ix+rec_iy=hp_i$

    此时可以EXGCD解出 $x$ 的最小正整数解 $x_0$。

    根据某定理(什么定理来着?)可得通解为 $x=x_0+frac{rec_i}{gcd(atk_i,rec_i)}k(kin N^+)$。

    于是就变成了 $xequiv x_0(operatorname{mod} frac{rec_i}{gcd{atk_i},rec_i})$。

    这一部分复杂度为 $O(nlog v)$。


    接下来就是裸的EXCRT了!

    这一部分复杂度为 $O(nlog v)$。


    等一下,还有一句提示没看到:

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

    突然慌张

    冷静分析一下,我们在各种算法中模数都有可能超过int范围,要是一乘……

    那……只能上龟速乘了。时间复杂度是没变,但是常数的话……

    幸好不是wys的题,不然时限就是1s了


    好吧,都说完了,上代码吧。(之前因为数据水没有注意 $xgemax(lceildfrac{hp_i}{atk_i} ceil)$)

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 typedef long long ll;
     4 const int maxn=100010;
     5 multiset<ll> mts;    //模拟拿剑
     6 int t,n,m;
     7 bool spec,exist,same;
     8 ll hp[maxn],rec[maxn],a[maxn],b[maxn];    //a,b表示最后方程组是x=a_i(mod b_i)
     9 int award[maxn],atk[maxn];    //award是奖励的剑
    10 ll qmul(ll a,ll b,ll mod){    //*速乘
    11     ll ans=0;
    12     for(;b;b>>=1,a=(a+a)%mod) if(b&1) ans=(ans+a)%mod;
    13     return ans;
    14 }
    15 ll exgcd(ll a,ll b,ll &x,ll &y){    //模板
    16     if(!b){x=1;y=0;return a;}
    17     ll d=exgcd(b,a%b,y,x);y-=a/b*x;return d;
    18 }
    19 ll gcd(ll a,ll b){    //(不要问我为什么有这个)
    20     if(!b) return a;
    21     return gcd(b,a%b);
    22 }
    23 int main(){
    24     scanf("%d",&t);
    25     while(t--){
    26         spec=false;same=exist=true;    //spec是rec=1,same是hp=rec,exist是有没有解
    27         mts.clear();
    28         scanf("%d%d",&n,&m);
    29         for(int i=1;i<=n;i++) scanf("%lld",hp+i);
    30         for(int i=1;i<=n;i++){scanf("%lld",rec+i);if(rec[i]<hp[i]) spec=true;}    //rec<hp?
    31         for(int i=1;i<=n;i++) scanf("%d",award+i);
    32         for(int i=1;i<=m;i++){
    33             int x;
    34             scanf("%d",&x);
    35             mts.insert(x);    //全部进去
    36         }
    37         for(int i=1;i<=n;i++){
    38             multiset<ll>::iterator it;
    39             if(hp[i]<*mts.begin()) it=mts.begin();    //如果没有比血量小的剑,则用第一把(最小的)
    40             else it=mts.upper_bound(hp[i]),it--;    //否则用第一个大于它的-1(即最后一个小于等于它的)
    41             atk[i]=*it;
    42             mts.erase(it);
    43             mts.insert(award[i]);    //拿走再选
    44         }
    45         if(spec){    //特判hp>rec
    46             ll ans=0;
    47             for(int i=1;i<=n;i++) ans=max(ans,ll(ceil(1.0*hp[i]/atk[i])));
    48             printf("%lld
    ",ans);
    49             continue;
    50         }
    51         for(int i=1;i<=n;i++){
    52             if(!atk[i]){puts("-1");exist=false;break;}    //无法攻击?无解
    53             if(hp[i]!=rec[i]) same=false;    //hp=rec?
    54             ll x,y,d=exgcd(atk[i],rec[i],x,y);    //解x0
    55             if(hp[i]%d){puts("-1");exist=false;break;}    //无解
    56             x=(x+rec[i])%rec[i];b[i]=rec[i]/d;a[i]=qmul(x,hp[i]/d,b[i]);    //真的x0推出b=rec/gcd(atk,rec),a=x0
    57         }
    58         if(same){    //特判hp=rec
    59             ll ans=ll(ceil(1.0*hp[1]/atk[1]));
    60             for(int i=2;i<=n;i++){
    61                 ll x=ceil(1.0*hp[i]/atk[i]),d=gcd(ans,x);
    62                 ans=ans/d*x;
    63             }
    64             printf("%lld
    ",ans);continue;
    65         }
    66         if(!exist) continue;
    67         ll ans=a[1],mod=b[1];    //开始EXCRT(以下模板,不解释,可以去我的另一个博客看)
    68         for(int i=2;i<=n;i++){
    69             ll x,y,d=exgcd(mod,b[i],x,y),r=((a[i]-ans)%b[i]+b[i])%b[i],tmp=mod/d*b[i];
    70             if(r%d){puts("-1");exist=false;break;}
    71             x=(x+b[i])%b[i];x=qmul(x,r/d,b[i]);
    72             ans=(qmul(mod,x,tmp)+ans)%tmp;
    73             mod=tmp;
    74         }
    75         if(!exist) continue;
    76         printf("%lld
    ",ans);    //终于没了
    77     }
    78 }
    NOI2018D2T1_dragon(EXCRT)

    终于啊……

  • 相关阅读:
    Server Tomcat v8.0 Server at localhost was unable to start within 45 seconds. If the server requires more time, try increasing the timeout in the server editor.
    用户画像——“打标签”
    python replace函数替换无效问题
    python向mysql插入数据一直报TypeError: must be real number,not str
    《亿级用户下的新浪微博平台架构》读后感
    【2-10】标准 2 维表问题
    【2-8】集合划分问题(给定要分成几个集合)
    【2-7】集合划分问题
    【2-6】排列的字典序问题
    【2-5】有重复元素的排列问题
  • 原文地址:https://www.cnblogs.com/1000Suns/p/9608901.html
Copyright © 2011-2022 走看看