【题解】[NOI2018] 屠龙勇士

solution:

毒瘤题。

注意我们只关注同余方程有解即可。

根据题意得到 a i + p i ∗ y = A T K ∗ x i a_i+p_i*y=ATK*x_i ai+piy=ATKxi

化简得到 A T K ∗ x i ≡ a i ( m o d p i ) ATK*x_i\equiv a_i\pmod {p_i} ATKxiai(modpi) ,这个式子等价于 A T K ≡ b i ( m o d p i ( p i , x i ) ) ATK\equiv b_i\pmod {\frac{p_i}{(p_i,x_i)}} ATKbi(mod(pi,xi)pi) ,其中 b i b_i bi 是满足 b i ∗ x i ≡ a i ( m o d p i ) b_i*x_i\equiv a_i\pmod {p_i} bixiai(modpi) 的最小非负整数解。

把上面的式子扔进 excrt 即可。注意 p i = 1 p_i=1 pi=1 特判。

时间复杂度 O(Tn(loglcm+logn)) 。

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define PII pair<int,int>
#define PIII pair<int,PII>
#define ll long long
#define INF 0x3f3f3f3f
using namespace std;
const int mx=1e5+5;
int n,q;
ll a[mx],m[mx],t[mx],p[mx],ad[mx];
multiset<ll> s;
ll gcd(ll x,ll y) {
	return y==0?x:gcd(y,x%y);
}
ll fmul(ll x,ll y,ll z) {
	x%=z,y%=z; if(y<0) x=-x,y=-y;
	ll sum(0);
	for(;y;y>>=1) {
		if(y&1) sum=(sum+x)%z;
		x=(x+x)%z; 
	}
	return sum;
}
ll lcm(ll x,ll y) {
	if(x==0||y==0) return x^y;
	return x/gcd(x,y)*y;
}
void exgcd(ll &x,ll &y,ll a,ll b,ll &d) {
	if(b==0) {
		x=1,y=0,d=a;
	}
	else {
		exgcd(y,x,b,a%b,d);
		y-=x*(a/b);
	}
}
bool excrt() {
	for(int i=1;i<=n;i++) {
		if(a[i]>=m[i]) return 1;
	}
	for(int i=2;i<=n;i++) {
		ll tmp=((a[i]-a[1])%m[i]+m[i])%m[i];
		ll k1,k2,d; exgcd(k1,k2,m[1],m[i],d);
		if(tmp%d) return 1;
		k1=(fmul(k1,tmp/d,m[i]/d)+(m[i]/d))%(m[i]/d);
		ll M=m[1]/d*m[i];
		a[1]=(a[1]+fmul(m[1],k1,M))%M;
		m[1]=M;
	}
	return 0;
}
void solve() {
	ll tmp(0);
	for(int i=1;i<=n;i++) {
		auto it=s.upper_bound(t[i]);
		if(it!=s.begin()) it--;
 		ll x=*it; s.erase(it); s.insert(ad[i]);
		if(t[i]%gcd(x,p[i])) {
			printf("-1\n");
			return;
		}
		//求解 x*k1=a (mod p[i]) 的最小非负整数
		//k1 周期为 p[i]/gcd(p[i],x) 
		ll k1,k2,d; exgcd(k1,k2,x,p[i],d);
		ll mo=p[i]/d;
		k1=(fmul(k1,t[i]/d,mo)+mo)%mo;
		//等价转化为 ATK=k1 (mod p[i]/d) 
		m[i]=mo;
		a[i]=k1;
		if(m[i]==1) tmp=max(tmp,(t[i]+x-1)/x);
	}
	if(excrt()) {
		printf("-1\n");
		return;
	}
	a[1]=max(a[1],tmp);
	printf("%lld\n",a[1]);
	
	return;
}
signed main() {
	int T; scanf("%d",&T);
	while(T--) {
		scanf("%d%d",&n,&q);  s.clear();
		for(int i=1;i<=n;i++) {
			scanf("%lld",&t[i]);
		}
		for(int i=1;i<=n;i++) {
			scanf("%lld",&p[i]);
		}
		for(int i=1;i<=n;i++) {
			scanf("%lld",&ad[i]);
		}
		for(int i=1;i<=q;i++) {
			ll x; scanf("%lld",&x);
			s.insert(x);
		}
		solve();
		
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值