[solution] Luogu-P4774 [NOI2018] Dragon Slayer

Posted by kobayashi_one on Fri, 03 Dec 2021 14:37:01 +0100

If you fight with a dragon for too long, you will become a dragon. If you stare at the abyss too long, the abyss will stare back.

​ —— Nietzsche's the other side of good and evil

P4774 [NOI2018] Dragon Slayer

\(\text{Description}\)

Players need to kill \ (n \) dragons in the order of number \ (1 \to n \), and each dragon has an initial health value \ (b_i \). At the same time, each dragon has the ability to recover, which will increase its health by \ (a_i \) each time until its health is non negative.

At the beginning of the game, the player has \ (m \) swords, and each sword has an attack power \ (atk_i \). Each time when facing the dragon, the player can only choose one sword. When the dragon is killed, the sword will disappear, but the player will get a brand-new sword.

Each time when facing the dragon, the player will choose the sword with the greatest attack power not higher than the dragon's initial HP as the weapon. If there is no such sword, choose the sword with the lowest attack power as the weapon.

Facing each dragon, players will attack the Dragon \ (x \) times (\ (x \) is a constant) with the selected sword, reducing the dragon's HP by \ (x \cdot atk \).

After that, the dragon will continue to use the recovery ability to recover \ (a_i \) HP each time. If its hp is \ (0 \) before recovery or after a recovery, the dragon will die and the player will pass the customs.

Now all the attributes of each dragon are given. It is requested to set the number of attacks \ (x \) of the robot to pass the game with the minimum number of attacks.

If you can't pass the game no matter how many settings are set, output \ (- 1 \).

\(\text{Solution}\)

The sword whose attack power is not higher than the dragon's initial HP is the most powerful weapon

It can be directly realized by using the balance tree or \ (\ rm multiset \). We set the attack power of this sword as \ (coe_i \).

So to make the life value \ (0 \) is actually to find the smallest non negative integer \ (x \), so that

\[\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} \]

For such a congruence equation \ (coe\cdot x\equiv b\pmod a \), directly use \ (\ rm exgcd \) to convert it into \ (x\equiv b'\pmod{\frac{a}{\gcd(coe,a)}} \).

Note that the modulus must be divided by \ (\ gcd(coe,a) \), otherwise the solution will be omitted. (this little Ao should have said all about it)

Then there is a \ (\ rm exCRT \).

Note: each dragon's blood volume must be negative before it can start to recover. Therefore, during processing, use a variable \ (mn \) to record at least how many attacks to attack, \ (mn = \ Max \ left \ {\ left \ lflow \ dfrac {b_i}{atk_i} \ right \ rflow \ right \} \), and then add a multiple of \ (\ operatorname{lcm}(a_1,a_2,\dots,a_n) \) to make \ (x\ge mn \).

It was such a thing that made me adjust for \ (5 \) days and made \ (6 \) individuals who did this question doubt life

\(\text{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; // Be sure to divide the modulus first
			b[i] /= Gcd;
			b[i] = (qmul(x, b[i], a[i]) + a[i]) % a[i]; // Re mold
		}
		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) // Maximum number less than or equal to 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;
}

After fighting with the Dragon slaying warrior for a long time, he also became the Dragon slaying warrior, that is, the dragon.

Topics: data structure Math