luogu4774 [NOI2018]屠龍勇士

題目鏈接

  • 可以想到第一次屠龍用的刀是確定的,可以列一個同餘方程xatk1a1=0(mod p1) x * atk_1 - a_1 = 0 (mod \space p_1) ,然後每次用的刀都可以確定,因此預先確定所有的刀,對100%的數據用EXCRT求解同餘方程組就可以了。

  • 怎麼預先確定所有的刀呢?直接寫的話可以離散然後用樹狀數組維護。。。但是有STL
    Multiset裏的lower_bound 和upper_bound

  • 使用exgcd求解一般的同餘方程Bx=A(mod p) Bx=A (mod \space p)這裏p可能不是質數,B和p可能不互質,上式可化成
    Bx+Py=ABx+Py=A
    從而用exgcd解出x即可。

  • EXCRT的注意事項:數據範圍都是long long的,直接乘會爆long long, 要用快速乘

  • 這個題的坑:用EXCRT直接求出來的最小正整數解可能不是答案,考慮同餘方程之外,題目要求龍的生命值(經過恢復)恰好爲0纔會死去,因此每條龍都有最小攻擊次數。答案應該是通解中滿足>=max{最小攻擊次數}的值。


只是做到理解具體步驟的程度,還不能完全自己寫出來,STL幾乎不會用,同餘化簡要推很久,exgcd,excrt只有有板子的時候纔會寫,會忘記寫快速乘

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <set>
using namespace std;
#define N 100005
typedef long long LL;
int T,n,m;
LL a[N],p[N],btk[N],atk[N],c[N], P;
LL qmul(LL a, LL b, LL P)
{
	LL res = 0;
	while (b)
	{
		if (b & 1) res = (res + a) % P;
		a = (a + a) % P;
		b >>= 1;
	}
	return res;
}
LL exgcd(LL a, LL b, LL & x, LL & y)
{
	if (b == 0) {x = 1;y = 0;return a;}
	LL d = exgcd(b, a % b, y, x);
	y -= (a / b) * x;
	return d;
}
LL excrt()
{
	LL M = p[1], ans = a[1], x, y;
	for (int i=1;i<=n;i++)
	{
		LL d = exgcd(M, p[i], x, y), c = ((a[i] - ans) % p[i] + p[i]);
		LL tmp = p[i] / d;
		if (c % d != 0) return -1;
		x = qmul(x, c / d, tmp);
		ans += x * M;
		M = M * tmp;
		ans = (ans % M + M) % M;
	}
	P = M;
	return ans;
}
LL solve()
{
	multiset<LL> S;
	for (int i=1;i<=m;i++)
		S.insert(atk[i]);
	multiset <LL>:: iterator it;
	LL tmp = 0;
	for (int i=1;i<=n;i++){
		it = (a[i] < (*S.begin())) ? S.begin():(--S.upper_bound(a[i]));
		c[i] = *it;
		S.erase(it);
		S.insert(btk[i]);
		tmp = max(tmp, ((a[i]-1) / c[i] + 1));//上取整
	}
	//化簡同餘方程
	for (int i=1;i<=n;i++)
	{
		LL x, y;
		LL d = exgcd(c[i], p[i], x, y);
		if (a[i] % d != 0) return -1;
		x = (x % p[i] + p[i]) % p[i];
		a[i] = qmul(x, a[i] / d, p[i]);//! 還是在模p[i]的意義下
		p[i] /= d;
	}
	LL ans = excrt();
	if (ans != -1 && ans < tmp)
		ans = ans + ((tmp - ans - 1) / P + 1) * P;//上取整
	return ans;
}
int main()
{
	scanf("%d", &T);
	while (T--)
	{
		scanf("%d%d", &n, &m);
		for (int i=1;i<=n;i++) scanf("%lld", &a[i]);
		for (int i=1;i<=n;i++) scanf("%lld", &p[i]);
		for (int i=1;i<=n;i++) scanf("%lld", &btk[i]);
		for (int i=1;i<=m;i++) {scanf("%lld", &atk[i]);}
		printf("%lld\n", solve());
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章