123789456ye

已AFO

[NOI2018]屠龙勇士

题面:Luogu
题解:
首先我们发现,每一次打longge龙的剑是可以算出来的,所以全部丢到\(multiset\)里就好了
于是我们要求解的就是

\[\begin{cases} K_1x\equiv a_1~(~mod~p_1) \\ K_2x\equiv a_2~(~mod~p_2) \\ K_3x\equiv a_3~(~mod~p_3) \\ \dots \end{cases} \]

我们考虑如何求一个方程

\[K_1x\equiv a_1~(~mod~p_1) \\ K_1x+P_1y=a_1~(\because a_1|\gcd(K_1,p_1)) \\ let~K_1Sx+P_1Sy=gcd \\ x\equiv \frac{a_1}{\gcd(K_1,p_1)}Sx~(~mod~\frac{p_1}{\gcd(K_1,p_1)}) \]

其中\(Sx\)可以\(exgcd\)求出来
然后我们把这些方程转化成了标准形式,套\(excrt\)即可


但是这题细节贼多
首先,如果某个时候\(a_i>p_i\),那么可能我们虽然没把这条龙打到0以下,但血量的确满足方程要求
这时候我们发现数据范围保证如果有这种情况,必定有\(p_i=1\)
所以直接一路取\(max\)即可,也就是

\[ans=\max_{i=1\sim n}(\left \lceil \frac{a_{i}}{K_i} \right \rceil) \]

其次,上面那个过程中,如果\(a_i\not|\gcd(K_i,p_i)\),就直接puts("-1")即可
\(excrt\)合并时也是如此
最后,这题中间会爆\(long~long\),所以要上龟速乘法
如果最后答案小于\(mx\),则要补到满足条件的最小值,也就是\(ans+m\left \lceil \frac{mx-ans}{m} \right \rceil\)

#include<bits/stdc++.h>
using namespace std;
template<typename T>
inline void read(T& x)
{
	x = 0; char c = getchar();
	while (!isdigit(c)) c = getchar();
	while (isdigit(c)) x = x * 10 + c - '0', c = getchar();
}
#define maxn 100005
#define ll long long
multiset<ll> s;
multiset<ll>::iterator it;
ll a[maxn], p[maxn], b[maxn];
ll x, y, Gcd;
ll exgcd(ll a, ll b, ll& x, ll& y)//求逆元,顺便返回gcd(a,b)
{
	if (!b) { x = 1, y = 0; return a; }
	ll tp = exgcd(b, a % b, y, x);
	y -= a / b * x;
	return tp;
}
inline ll qmul(ll a, ll b, ll p)
{
	ll ans = 0;
	for (; b; b >>= 1, a = (a + a) % p)
		if (b & 1) ans = (ans + a) % p;
	return ans;
}
int main()
{
	ll T, n, m, mx, ans, tp, k;
	read(T);
	while (T--)
	{
		read(n), read(m); s.clear();
		for (int i = 1; i <= n; ++i) read(a[i]);
		for (int i = 1; i <= n; ++i) read(p[i]);
		for (int i = 1; i <= n; ++i) read(b[i]);
		for (int i = 1; i <= m; ++i) read(tp), s.insert(tp);
		mx = ans = 0; m = 1;
		for (int i = 1; i <= n; ++i)
		{
			it = s.upper_bound(a[i]);
			if (it != s.begin()) --it;
			k = *it;
			s.erase(it); s.insert(b[i]);
			mx = max(mx, 1ll * (a[i] - 1) / k + 1);
			k %= p[i], a[i] %= p[i];
			if (!k && a[i]) { puts("-1"); goto End; }
			if (!k && !a[i]) continue;//这个方程没用,直接跳掉
			Gcd = exgcd(k, p[i], x, y);//求出gcd(K_i,p_i)和一个特解
			if (a[i] % Gcd) { puts("-1"); goto End; }
			p[i] /= Gcd;
			a[i] = qmul(a[i] / Gcd, (x % p[i] + p[i]) % p[i], p[i]);//转化方程
			Gcd = exgcd(m, p[i], x, y);
			if ((a[i] - ans) % Gcd) { puts("-1"); goto End; }
			m = m / Gcd * p[i];
			ans = (ans + qmul(qmul(m / p[i], ((a[i] - ans) % m + m) % m, m), (x % m + m) % m, m)) % m;//合并
		}
		printf("%lld\n", ans >= mx ? ans : ans + m * ((mx - ans - 1) / m + 1));
	End:;
	}
	return 0;
}
posted @ 2020-03-29 11:42  123789456ye  阅读(81)  评论(0编辑  收藏  举报