[NOI2018] 屠龙勇士

前言

属实是板题了,竟然没做过,本想rush一波结果怎么都过不了样例,然后去模板那里测发现也过不了样例,看半天原来是板子写挂了,相当尴尬。

题目

洛谷

讲解

由于 打着太麻烦,所以下文直接用 = 了。

可以发现每头龙对应的剑是唯一确定的,所以用 \(\operatorname{multiset}\) 直接求出所有龙对应的剑,记其攻击力为 \(A_i\)。

然后我们观察一下数据范围,当不满足 \(a_i\le p_i\) 时,一定有 \(p_i=1\),此时答案为 \(\max\{\lceil\frac{a_i}{A_i}\rceil\}\),至于为什么出题人这么设计,我们接下来马上解答。

现在考虑 \(a_i\le p_i\) 的情况,我们其实是要求方程组 \(A_iX=a_i\pmod {p_i}\) 的解,可以发现这个方程对这道题成立的条件正是 \(a_i\le p_i\),或者说是 \(a_i<p_i\),因为初始血量 \(a_i\) 是不能凭空减少的,所以要求 \(a_i<p_i\),在这道题里面 \(a_i=p_i\) 的情况还要特判(但是数据太水不特判也能过)。

对于方程组 \(A_iX=a_i\pmod{p_i}\) 我们能很快想到扩展中国剩余定理,但是随即又想到它只能处理 \(X\) 前面没有系数的情况,那可怎么办呢?显然不能直接把 \(A_i\) 除到右边去,因为 \(A_i\) 在 \(\mod p_i\) 下未必有逆元。

把这个式子写成一般形式:\(p_it-A_iX=\gcd(p_i,A_i)\),用扩欧求出特解 \(t',X'\) 之后,我们就可以得到 \(X\) 的通解了:\(X=X'+k\times \frac{p_i}{\gcd(p_i,A_i)}\)

原式子的通解 \(X=X'\times \frac{a_i}{\gcd(p_i,A_i)}+k\times \frac{p_i}{\gcd(p_i,A_i)}\)

换成同余方程就是 \(X=X'\times \frac{a_i}{\gcd(p_i,A_i)}\pmod {\frac{p_i}{\gcd(p_i,A_i)}}\)

然后用扩展中国剩余定理合并即可。

代码

//12252024832524
#include <bits/stdc++.h>
#define TT template<typename T>
using namespace std; 

typedef long long LL;
const int MAXN = 100005;
int n,m;
LL a[MAXN],p[MAXN],aw[MAXN],A[MAXN],r[MAXN];
multiset<LL> s;

LL Read()
{
	LL x = 0,f = 1;char c = getchar();
	while(c > '9' || c < '0'){if(c == '-')f = -1;c = getchar();}
	while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
	return x * f;
}
TT void Put1(T x)
{
	if(x > 9) Put1(x/10);
	putchar(x%10^48);
}
TT void Put(T x,char c = -1)
{
	if(x < 0) putchar('-'),x = -x;
	Put1(x); if(c >= 0) putchar(c);
}
TT T Max(T x,T y){return x > y ? x : y;}
TT T Min(T x,T y){return x < y ? x : y;}
TT T Abs(T x){return x < 0 ? -x : x;}

LL gsc(const LL x,const LL y,const LL MOD)
{
	LL ret = x*y - (LL)((long double)x/MOD*y+0.5)*MOD;
	return (ret%MOD+MOD)%MOD;
} 
LL exgcd(LL a,LL b,LL &x,LL &y)
{
	if(!b) {x = 1;y = 0;return a;}
	LL ret = exgcd(b,a%b,y,x);
	y -= a/b*x; return ret;
}

int main()
{
//	freopen("dragon.in","r",stdin);
//	freopen("dragon.out","w",stdout);
	for(int T = Read(); T ;-- T)
	{
		bool sp = 1; s.clear();
		n = Read(); m = Read();
		for(int i = 1;i <= n;++ i) a[i] = Read();
		for(int i = 1;i <= n;++ i) p[i] = Read(),sp &= p[i] == 1;
		for(int i = 1;i <= n;++ i) aw[i] = Read();
		for(int i = 1;i <= m;++ i) s.insert(Read());
		for(int i = 1;i <= n;++ i)
		{
			auto it = s.upper_bound(a[i]);
			if(it != s.begin()) --it;
			A[i] = *it; s.erase(it);
			s.insert(aw[i]);
		}
		if(sp)
		{
			LL ans = 0;
			for(int i = 1;i <= n;++ i) ans = Max(ans,(a[i]+A[i]-1)/A[i]);
			Put(ans,'\n');
		}
		else
		{
			bool gg = 0;//G!
			for(int i = 1;i <= n;++ i)
			{
				LL x,y,d; d = exgcd(p[i],A[i],x,y); p[i] /= d; A[i] /= d;
				if(a[i] % d) {gg = 1;break;} a[i] /= d;
				r[i] = gsc(y,a[i],p[i]);
			}
			for(int i = 1;i < n && !gg;++ i)
			{
				if(r[i] > r[i+1]) swap(r[i],r[i+1]),swap(p[i],p[i+1]);
				LL x,y,d,cha = r[i+1]-r[i]; d = exgcd(p[i],p[i+1],x,y);
				if(cha % d) {gg = 1;break;}
				//p1x-p2y=r2-r1
				p[i+1] = p[i] / d * p[i+1]; x = gsc(x,cha/d,p[i+1]);
				r[i+1] = (r[i] + gsc(x,p[i],p[i+1])) % p[i+1];
			}
			if(gg) {Put(-1,'\n');continue;}
			Put(r[n]>0?r[n] : p[n],'\n');
		}
	}
	return 0;
}
上一篇:二叉搜索树(C++)


下一篇:蓝桥杯-蓝跳跳(矩阵快速幂 70%数据)