[LOJ#2721] [NOI2018] 屠龙勇士 [ExCRT]

Link-Cut-Tree
LOJ - https://loj.ac/problem/2721
Luogu - https://www.luogu.org/problemnew/show/P4774
BZOJ - https://www.lydsy.com/JudgeOnline/problem.php?id=5418


Deskappation
n n n 条巨da的黑炎龙(Darkflame ’s 玛斯塔 \tiny\colorbox{#000000}{\color{#000000}{'s 玛斯塔}} s ) 每条黑炎龙都有 a i a_i ai 的生命值
+ 一条黑炎龙当且仅当HP为 0 0 0 的时候会低头
   - 每条黑炎龙如果HP为负数就可以自动恢♂复HP直到非负。
   - 每次恢复 p p p 点HP。可以认为它恢复得非常快,只要负了、一瞬间就会回血到非负。
+ 玩家有 m m m 把大保健,杀死每只龙会少一条大保健,然后龙会喷装屠龙宝刀点击就送一刀999

小DD颓这个游戏的时候觉得推起来索然无味,但是最快通关介款游戏你就可以AK ION8102
然后小DD化身机器人认识它(指苣龙)的美

+ 小DD会每次单推一条龙;
   - 我们钦定了小DD必须按照 1 ∼ n 1\sim n 1n 的顺序单推
+ 每一轮单推,小DD一出生(指开始屠一条龙),他会选攻击力不高于苣龙初始HP的剑里面攻击力最大的一把;
   - 如果没有,就选攻击力最低的
+ 作为冷酷的复读机器人,单推巨龙的时候小DD会用他选好的大保健连续攻击 x x x
   - 当然显然肯定一定会产生 x ⋅ A T K x\cdot ATK xATK 的DMG

求复读机最少复读几次公鸡打鸣 咕咕咕 \tiny\colorbox{#000000}{\color{#000000}{咕咕咕}} 之后能够通关
如果无法通关,输出 − 1 -1 1 ,表示复读机会一直复读下去(快乐)
如果无法通关,输出 − 1 -1 1 ,表示复读机会一直复读下去(快乐)
如果无法通关,输出 − 1 -1 1 ,表示复读机会一直复读下去(快乐)
如果无法通关,输出 − 1 -1 1 ,表示复读机会一直复读下去(快乐)
(快乐)
(笔芯)

测试点编号 n m p_i a_i 攻击力其他限制
1 \le 10^5 =1 \le 10^5 =1
2
3 \le 10^5
4
5 \le 10^3 \le 10^5 特性 1、特性 2
6
7
8 =1 \le 10^8 \le 10^8 \le 10^6 特性 1
9
10
11
12
13
14 =10^5 =1 无特殊限制
15
16 \le 10^5 所有 p_i 是质数 \le 10^{12} 特性 1
17
18无特殊限制
19
20

AnalysⅠs
注意到所有的数据必定满足 BUG1 或者 p i = 1 p_i=1 pi=1

Feature1: ∀ i , a i ≤ p i \forall i,a_i\le p_i i,aipi

首先显然可以列一个方程组
{ a i − x j ⋅ a t k j + k ⋅ p i = 0 \begin{cases} \begin{array}{rcl}a_i-x_j\cdot atk_j+k\cdot p_i=0 \end{array} \end{cases} {aixjatkj+kpi=0
哦(冷漠)

满足 ∀ i , a i ≤ p i \forall i,a_i\le p_i i,aipi 或者 p i = 1 p_i=1 pi=1
那么 a i ≡ x ⋅ a t k j ( m o d p i ) a_i\equiv x\cdot atk_j\pmod{p_i} aixatkj(modpi) 或者 x ≥ ⌈ a i a t k j ⌉ x\ge\Large\lceil\frac{a_i}{atk_j}\rceil xatkjai

后面那个处理一下,
对前面那个联立线性同余方程组ExCRT即可。
(实际上可以分类处理,……不难)

a i ≡ x ⋅ a t k j ( m o d p i ) a_i\equiv x\cdot atk_j\pmod{p_i} aixatkj(modpi)
x ≡ a i ⋅ i n v ( a t k j ) ( m o d p i ) x\equiv a_i\cdot inv(atk_j)\pmod{p_i} xaiinv(atkj)(modpi)
需要注意的是 i n v ( a t k j ) inv(atk_j) inv(atkj) 不一定存在。(看到逆元就应该注意这个)
只需把 a i , a t k j , p i a_i,atk_j,p_i ai,atkj,pi 同除它们的GCD即可……还是不行就无解了

有一个问题是 a t k j atk_j atkj 怎么搞来啊……
……
……这个显然可以预处理嘛………………


没错这道题的确就完了,不要怀疑
(我打代码好慢啊
像cxk

#include<cstdio>
#include<cstdlib>
#include<cctype>
#include<queue>
#include<set>
#include<ctime>
#include<cmath>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN = 114514;
int T, n, m;
long long a[MAXN];
long long p[MAXN];
long long Rew[MAXN];
long long Atk[MAXN];
long long atk[MAXN];
bool csk;
inline long long Mul(const long long& a, const long long& b, const long long& p)
{
	static long long t;
	t = (long double) a * b / p;
	return a * b - t * p;
}
inline long long Adjust(const long long& x, const long long& p)
{
	static long long t;
	t = x % p + p;
	return (t >= p) ? (t - p) : t;
}
inline long long gcd(const long long& a, const long long& b)
{
	return !b?a:gcd(b, a%b);
}
inline void exgcd(long long a, long long b, long long& d,long long& x, long long& y)
{
	!b?(d=a,x=1,y=0):(exgcd(b,a%b,d,y,x),y-=x*(a/b));
}
inline long long inv(const long long& X, const long long& P)
{
	static long long d, x, y;
	exgcd(X, P, d, x, y);
	return Adjust(x,  P);
}
inline void Input()
{
	csk = 0;
	scanf("%d%d", &n, &m);
	for (register int i = 1; i <= n; ++i)
	{
		scanf("%lld", &a[i]);
	}
	for (register int i = 1; i <= n; ++i)
	{
		scanf("%lld", &p[i]);
		if(p[i]!=1)csk=1;
	}
	for (register int i = 1; i <= n; ++i)
	{
		scanf("%lld", &Rew[i]);
	}
	for (register int i = 1; i <= m; ++i)
	{
		scanf("%lld", &Atk[i]);
	}
}
multiset<long long>List;
inline bool Decide()
{
	List.clear();
	for (register int i = 1; i <= m; ++i) List.insert(Atk[i]);
	register multiset<long long>::iterator iter;
	for (register int i = 1; i <= n; ++i)
	{
		if(List.empty()) return 1;
		iter = List.upper_bound(a[i]);
		if(iter!=List.begin()) --iter;
		atk[i] = *iter;
		List.erase(iter);
		List.insert(Rew[i]);
	}
	return 0;
}
long long Ans, Lim;
bool liv[MAXN];
bool gaq;
inline void Init()
{
	gaq = 0;
	Lim = 0;
	memset(liv, 0, sizeof(liv));
	Ans = 0;
}
inline bool Structure()
{
	static long long tem, temtem;
	register bool fafa = 0;
	Init();
	for (register int i = 1; i <= n; ++i)
	{
		if (!csk)
		{
			Lim = max(Lim, (long long)ceil(1.0 * a[i] / atk[i]));
		}
		else
		{
			fafa = 1;
			tem = gcd(atk[i], p[i]);
			temtem = gcd(tem, a[i]);
			a[i] /= temtem; atk[i] /= temtem; p[i] /= temtem;
			if (temtem<tem) return 1;
			a[i] = Mul(a[i], inv(atk[i], p[i]), p[i]);
		}
	}
	if(!csk)
	{
		gaq = 1;
		printf("%lld\n", Lim);
		return 1;
	}
	return 0;
}
inline bool ExCRT()
{
	static long long A, M, k, K, G, T;
	A = a[1], M = p[1];
	for (register int i = 2; i <= n; ++i)
	{
		exgcd(M, p[i], G, K, k);
		T=A-a[i];
		if (T%G) return 1;
		K = Mul(K, (T/G), p[i]);
		T = M;
		M = M*(p[i]/G);
		A = Adjust(A - Mul(K, T, M), M);
	}
	printf("%lld\n", A);
	return 0;
}
int main()
{
	freopen("dragon.in","r",stdin);
	freopen("dragon.out","w",stdout);
	scanf("%d", &T);
	while (T--)
	{
		Input();
		if (Decide())
		{
			printf("-1\n");
			continue;
		}
		if (Structure())
		{
			if(gaq) continue;
			printf("-1\n");
			continue;
		}
		if (ExCRT())
		{
			printf("-1\n");
			continue;
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值