ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

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

2021-12-03 19:34:22  阅读:154  来源: 互联网

标签:巨龙 val int 题解 ll pos merge P4774 NOI2018


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

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

P4774 [NOI2018] 屠龙勇士

\(\text{Description}\)

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

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

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

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

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

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

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

\(\text{Solution}\)

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

直接用平衡树或 \(\rm multiset\) 实现即可,我们设这把剑的攻击力为 \(coe_i\)。

那么要使生命值为 \(0\),其实就是求最小的非负整数 \(x\),使得

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

对于这样一个同余方程 \(coe\cdot x\equiv b\pmod a\),直接用 \(\rm exgcd\) 将其转化成 \(x\equiv b'\pmod{\frac{a}{\gcd(coe,a)}}\) 即可。

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

接着就是一个 \(\rm exCRT\) 了。

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

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

\(\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; // 一定要先除模数
			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;
}

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

标签:巨龙,val,int,题解,ll,pos,merge,P4774,NOI2018
来源: https://www.cnblogs.com/mangoworld/p/Solution-Luogu-P4774.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有