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;
}
posted @ 2018-12-06 13:35  Romeolong  阅读(158)  评论(0编辑  收藏  举报