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)

    终于啊……

  • 相关阅读:
    VUe兄弟通信
    vue组件之间的通信, 父子组件通信,兄弟组件通信
    js 数组 map方法
    vue生命周期
    使用JQuery获取被选中的checkbox的value值
    C#中struct和class的区别详解 (转载)
    C# 增加时间的三个方法
    c# 事件3
    C# 事件2
    C# 事件
  • 原文地址:https://www.cnblogs.com/1000Suns/p/9608901.html
Copyright © 2011-2022 走看看