【题解】Luogu-P4774 [NOI2018] 屠龙勇士

43 篇文章 0 订阅
8 篇文章 0 订阅

与恶龙缠斗过久,自身亦成为恶龙。凝视深渊过久,深渊将回以凝视。

​ ——尼采《善恶的彼岸》

P4774 [NOI2018] 屠龙勇士

Description \text{Description} Description

玩家需要按照编号 1 → n 1 \to n 1n 顺序杀掉 n n n 条巨龙,每条巨龙拥有一个初始的生命值 b i b_i bi。同时每条巨龙拥有恢复能力,会使它的生命值每次增加 a i a_i ai,直至生命值非负。

游戏开始时玩家拥有 m m m 把剑,每把剑拥有一个攻击力 a t k i atk_i atki,每次面对巨龙时,玩家只能选择一把剑,当杀死巨龙后这把剑就会消失,但玩家会获得全新的一把剑。

每次面对巨龙时,玩家会选择攻击力不高于巨龙初始生命值中攻击力最大的一把剑作为武器。如果没有这样的剑,则选择 攻击力最低 的一把剑作为武器。

面对每条巨龙,玩家都会使用选择的剑攻击巨龙 x x x 次( x x x 为常数),使巨龙的生命值减少 x ⋅ a t k x \cdot atk xatk

之后,巨龙会不断使用恢复能力,每次恢复 a i a_i ai 生命值。若在恢复前或某一次恢复后其生命值为 0 0 0,则巨龙死亡,玩家通关。

现在给出了每条巨龙的所有属性,请求出应该将机器人的攻击次数 x x x 设置为多少,才能用最少的攻击次数通关游戏。

如果无论设置成多少都无法通关游戏,输出 − 1 -1 1

Solution \text{Solution} Solution

攻击力不高于巨龙初始生命值中攻击力最大的一把剑作为武器

直接用平衡树或 m u l t i s e t \rm multiset multiset 实现即可,我们设这把剑的攻击力为 c o e i coe_i coei

那么要使生命值为 0 0 0,其实就是求最小的非负整数 x x x,使得
{ c o e 1 ⋅ x ≡ b 1 ( m o d a 1 ) c o e 2 ⋅ x ≡ b 2 ( m o d a 2 ) ⋯ c o e n ⋅ x ≡ b n ( m o d a n ) \begin{cases} coe_1\cdot x\equiv b_1\pmod {a_1}\\ coe_2\cdot x\equiv b_2\pmod {a_2}\\ \cdots\\ coe_n\cdot x\equiv b_n\pmod {a_n} \end{cases} coe1xb1(moda1)coe2xb2(moda2)coenxbn(modan)
对于这样一个同余方程 c o e ⋅ x ≡ b ( m o d a ) coe\cdot x\equiv b\pmod a coexb(moda),直接用 e x g c d \rm exgcd exgcd 将其转化成 x ≡ b ′ ( m o d a gcd ⁡ ( c o e , a ) ) x\equiv b'\pmod{\frac{a}{\gcd(coe,a)}} xb(modgcd(coe,a)a) 即可。

注意模数一定要除以 gcd ⁡ ( c o e , a ) \gcd(coe,a) gcd(coe,a),不然会漏解。(这个小奥应该都讲了)

接着就是一个 e x C R T \rm exCRT exCRT 了。

注意事项:每条龙的血量必须是负数才会开始回血,所以在处理的时候用一个变量 m n mn mn 记录至少要攻击多少下, m n = max ⁡ { ⌊ b i a t k i ⌋ } mn=\max\left\{\left\lfloor\dfrac{b_i}{atk_i}\right\rfloor\right\} mn=max{atkibi},然后将解出来的 x x x 加上一个 lcm ⁡ ( a 1 , a 2 , … , a n ) \operatorname{lcm}(a_1,a_2,\dots,a_n) lcm(a1,a2,,an) 的倍数使得 x ≥ m n x\ge mn xmn 即可。

就是这样一个东西让我调了 5 5 5 天并让做这道题的 6 6 6 个人全部怀疑人生

Code \text{Code} Code

//18 = 9 + 9 = 18.
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <cstring>
#define Debug(x) cout << #x << "=" << x << endl
typedef long long ll;
using namespace std;

const int MAXN = 1e5 + 5;

ll a[MAXN], b[MAXN], coe[MAXN];

namespace Math
{
	ll div_ceil(ll x, ll y)
	{
		return (x - 1) / y + 1;
	}
	
	ll x, y;
	
	ll exgcd(ll a, ll b)
	{
		if (!b)
		{
			x = 1, y = 0;
			return a;
		}
		ll Gcd = exgcd(b, a % b);
		ll tmp = x;
		x = y;
		y = tmp - a / b * y;
		return Gcd;
	}
	
	ll qmul(ll a, ll b, ll p)
	{
		if (b < 0)
		{
			a = -a;
			b = -b;
		}
		ll base = a, ans = 0;
		while (b)
		{
			if (b & 1)
			{
				ans = (ans + base) % p;
			}
			base = (base + base) % p;
			b >>= 1;
		}
		return (ans + p) % p;
	}
	
	int pre(int n)
	{
		for (int i = 1; i <= n; i++)
		{
			ll Gcd = exgcd(coe[i], a[i]);
			if (b[i] % Gcd)
			{
				return -1;
			}
			a[i] /= Gcd; // 一定要先除模数
			b[i] /= Gcd;
			b[i] = (qmul(x, b[i], a[i]) + a[i]) % a[i]; // 再取模
		}
		return 1;
	}
	
	ll A, B;
	
	int merge(ll a, ll b)
	{
		ll Gcd = exgcd(A, a), c = B - b;
		if (c % Gcd)
		{
			return -1;
		}
		c /= Gcd;
		x = (qmul(x, c, a) + a) % a;
		ll Lcm = A / Gcd * a;
		B = (B - qmul(A, x, Lcm) + Lcm) % Lcm;
		A = Lcm;
		return 1;
	}
	
	ll exCRT(int n, ll mn)
	{
		if (pre(n) == -1)
		{
			return -1;
		}
		A = a[1], B = b[1];
		for (int i = 2; i <= n; i++)
		{
			if (merge(a[i], b[i]) == -1)
			{
				return -1;
			}
		}
		if (B < mn)
		{
			B += div_ceil(mn - B, A) * A;
		}
		return B;
	}
}
using Math::div_ceil;
using Math::exCRT;

namespace BST
{
	int tot, rt;
	
	struct treap
	{
		int lson, rson;
		ll val;
		int key;
	}t[MAXN << 1];
	
	void init()
	{
		tot = rt = 0;
		memset(t, 0, sizeof(t));
	}
	
	int build(ll val)
	{
		t[++tot] = treap{0, 0, val, rand()};
		return tot;
	}
	
	void split(int pos, ll val, int &a, int &b)
	{
		if (!pos)
		{
			a = b = 0;
			return;
		}
		if (t[pos].val <= val)
		{
			a = pos;
			split(t[pos].rson, val, t[a].rson, b);
		}
		else
		{
			b = pos;
			split(t[pos].lson, val, a, t[b].lson);
		}
	}
	
	int merge(int a, int b)
	{
		if (!a || !b)
		{
			return a + b;
		}
		if (t[a].key > t[b].key)
		{
			t[a].rson = merge(t[a].rson, b);
			return a;
		}
		else
		{
			t[b].lson = merge(a, t[b].lson);
			return b;
		}
	}
	
	void insert(ll val)
	{
		int a = 0, b = 0, c = build(val);
		split(rt, val, a, b);
		rt = merge(merge(a, c), b);
	}
	
	void remove(ll val)
	{
		int a = 0, b = 0, c = 0;
		split(rt, val, a, b);
		split(a, val - 1, a, c);
		c = merge(t[c].lson, t[c].rson);
		rt = merge(merge(a, c), b);
	}
	
	ll getpre(ll val) // 小于等于 val 的最大数
	{
		int a = 0, b = 0;
		split(rt, val, a, b);
		int pos = a;
		while (t[pos].rson)
		{
			pos = t[pos].rson;
		}
		rt = merge(a, b);
		return t[pos].val;
	}
	
	ll getmin()
	{
		int pos = rt;
		while (t[pos].lson)
		{
			pos = t[pos].lson;
		}
		return t[pos].val;
	}
}
using BST::init;
using BST::insert;
using BST::remove;
using BST::getpre;
using BST::getmin;

int n, m;
ll mn;
ll atk[MAXN], rew[MAXN];

void read()
{
	for (int i = 1; i <= n; i++)
	{
		scanf("%lld", b + i);
	}
	for (int i = 1; i <= n; i++)
	{
		scanf("%lld", a + i);
	}
	for (int i = 1; i <= n; i++)
	{
		scanf("%lld", rew + i);
	}
	for (int i = 1; i <= m; i++)
	{
		scanf("%lld", atk + i);
	}
}

void cal()
{
	init();
	for (int i = 1; i <= m; i++)
	{
		insert(atk[i]);
	}
	mn = 0;
	for (int i = 1; i <= n; i++)
	{
		ll res = getpre(b[i]);
		if (res)
		{
			coe[i] = res;
		}
		else
		{
			coe[i] = getmin();
		}
		mn = max(mn, div_ceil(b[i], coe[i]));
		remove(coe[i]);
		insert(rew[i]);
	}
}

int main()
{
//	freopen("dragon.in", "r", stdin);
//	freopen("dragon.out", "w", stdout);
	srand((unsigned)time(NULL));
	int t;
	scanf("%d", &t);
	while (t--)
	{
		scanf("%d%d", &n, &m);
		read();
		cal();
		printf("%lld\n", exCRT(n, mn));
	}
	return 0;
}

与《屠龙勇士》缠斗过久,自身亦成为屠龙勇士,也就是那只恶龙。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值