NOI2018 屠龙勇士

一道简单的\(EXCRT\)
题目链接


解析

我们愉快地发现,攻击每条龙的剑的攻击力是确定的.
只用一个\(Multiset\)来维护攻击力即可.
然后,我们可以把这个题目转化一下.
假设打第\(i\)条龙时攻击力是\(t_i\)
\(t_ix\equiv a_i(mod\ p_i)\)
所以只要求解这个方程组即可.


等等!
这只能在\(a_i<p_i\)的情况下成立.
假设\(a_i>p_i\),那么可能还没把龙的血量打到\(0\)就已经是\(p\)的倍数了.
怎么办?
情况变得复杂起来了

然而,我们仔细地看一看表格
发现当\(a_i>p_i\)时,所有的\(p_i\)都为\(1\)
那么只要特判一下就好了.


那么我们只要求解\(t_ix\equiv a_i(mod\ p_i)\)这个方程组.
我们普通的\(EXCRT\)只能求解\(t_i=1\)的情况,那么这个该如何处理呢?
很简单,我们列出不定方程\(tx+py=a\)(为了方便,以后所有的\(t_i\)等等省略\(i\))求出\(g=gcd(t,p)\)
然后用\(exgcd\)求解这个不定方程.当然啦,如果\(a\not\equiv0(mod\ g)\),那么就输出\(-1\).
假设这个方程有解,我们求出了一组解\(tx,ty\)
用一个据说是叫做裴蜀定理的东西求出方程通解\(x=tx+k\frac{p}{g}\)
两边对\(\frac{p}{g}\)取模后得\(x\equiv tx(mod \frac{p}{g})\)
我们就成功化简了这个式子!
剩下的事情就很简单了,直接把\(excrt\)的板子套上去即可.
\(EXCRT\)的讲解戳这里
还有最重要的一点就是——注意龟速乘

代码如下

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<set>
#define N (100010)
#define inf (0x7f7f7f7f)
#define rg register int
#define Label puts("NAIVE")
#define GG ({gg=1,print(-1),ent;break;})
#define spa print(' ')
#define ent print('\n')
#define rand() (((rand())<<(15))^(rand()))
typedef long double ld;
typedef long long LL;
typedef unsigned long long ull;
using namespace std;
inline char read(){
    static const int IN_LEN=1000000;
    static char buf[IN_LEN],*s,*t;
    return (s==t?t=(s=buf)+fread(buf,1,IN_LEN,stdin),(s==t?-1:*s++):*s++);
}
template<class T>
inline void read(T &x){
    static bool iosig;
    static char c;
    for(iosig=false,c=read();!isdigit(c);c=read()){
        if(c=='-')iosig=true;
        if(c==-1)return;
    }
    for(x=0;isdigit(c);c=read())x=((x+(x<<2))<<1)+(c^'0');
    if(iosig)x=-x;
}
inline char readchar(){
    static char c;
    for(c=read();!isalpha(c);c=read())
    if(c==-1)return 0;
    return c;
}
const int OUT_LEN = 10000000;
char obuf[OUT_LEN],*ooh=obuf;
inline void print(char c) {
    if(ooh==obuf+OUT_LEN)fwrite(obuf,1,OUT_LEN,stdout),ooh=obuf;
    *ooh++=c;
}
template<class T>
inline void print(T x){
    static int buf[30],cnt;
    if(x==0)print('0');
    else{
        if(x<0)print('-'),x=-x;
        for(cnt=0;x;x/=10)buf[++cnt]=x%10+48;
        while(cnt)print((char)buf[cnt--]);
    }
}
inline void flush(){fwrite(obuf,1,ooh-obuf,stdout);}
multiset<LL>S;
set<LL>::iterator it;
int T,n,m;
LL c[N],a[N],p[N],rew[N];
LL gcd(LL a,LL b){
    if(!b)return a;
    return gcd(b,a%b);
}
void exgcd(LL a,LL b,LL &x,LL &y){
    if(!b){x=1,y=0;return;}
    exgcd(b,a%b,y,x),y-=(a/b)*x;
}
LL mult(LL a,LL b,LL mod){
    LL res=0,fu=1;
    if(a<0)fu=-fu,a=-a;
    if(b<0)fu=-fu,b=-b;
    while(b){
        if(b&1)res=(res+a)%mod;
        a=(a+a)%mod,b>>=1;
    }
    res*=fu;
    if(res<0)(res+=((-res-1)/mod+1)*mod);
    return res;
}
LL inv(LL a,LL b){
    LL x,y;
    exgcd(a,b,x,y);
    return (x<=0)?(x+b):x;
}
bool spj(){
    for(int i=1;i<=n;i++)
    if(p[i]>1)return 0;
    LL x=0;
    for(int i=2;i<=n;i++)
    x=max(x,(c[i]-1)/a[i]+1);
    print(x),ent;
    return 1;
}
int main(){
    read(T);
    while(T--){
        bool gg=0;
        read(n),read(m),S.clear();
        for(int i=1;i<=n;i++)read(c[i]);
        for(int i=1;i<=n;i++)read(p[i]);
        for(int i=1;i<=n;i++)read(rew[i]);
        for(int i=1;i<=m;i++){
            LL x;
            read(x),S.insert(x);
        }
        for(int i=1;i<=n;i++){
            it=S.upper_bound(c[i]);
            if(it==S.begin())a[i]=*it;
            else it--,a[i]=*it;
            S.erase(it),S.insert(rew[i]);
        }
        if(spj())continue;
        for(int i=1;i<=n;i++)a[i]%=p[i];
        for(int i=1;i<=n;i++)
        if(a[i]==0){
            if(p[i]==c[i])
            a[i]=1,p[i]=1,c[i]=0;
            else GG;
        }
        if(gg)continue;
        for(int i=1;i<=n;i++){
            LL A=a[i],C=c[i],P=p[i];
            LL g=gcd(A,P);if(C%g!=0)GG;
            LL tx,ty; exgcd(A,P,tx,ty);
            P/=g,tx=(tx%P+P)%P,C=mult(tx,C/g,P);
            a[i]=A,c[i]=C,p[i]=P;
        }
        if(gg)continue;
        LL c1=c[1],p1=p[1];
        for(int i=2;i<=n;i++){
            LL mo=p[i],c2=c[i];
            LL t=gcd(p1,mo),s=inv(p1/t,mo/t),tc=c1,tm=p1;
            if((c2-c1)%t!=0)GG;
            p1=(mo/t*tm),c1=(tc+mult(tm,mult(s,(c2-c1)/t,(mo/t)),p1))%p1;
        }
        if(gg)continue;
        if(c1<0)c1+=((-c1-1)/p1+1)*p1;
        print(c1%p1),ent;
    }
    return flush(),0;
}

转载于:https://www.cnblogs.com/Romeolong/p/10076264.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值