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;
}