首页 > 其他分享 >[NOI 2018] 屠龙勇士 做题笔记

[NOI 2018] 屠龙勇士 做题笔记

时间:2024-10-25 11:12:21浏览次数:1  
标签:生命 巨龙 NOI 攻击力 int times 2018 ans 屠龙

传送门

前言

我是唐诗,考场没开这题。导致看都没看,直接考试策略大失误。其实知道了这题是个扩展中国剩余定理之后就很好做了。

题意

非常奇妙。

小 D 最近在网上发现了一款小游戏。游戏的规则如下:

  • 游戏的目标是按照编号 \(1 \rightarrow n\) 顺序杀掉 \(n\) 条巨龙,每条巨龙拥有一个初始的生命值 \(a_i\) 。同时每条巨龙拥有恢复能力,当其使用恢复能力时,它的生命值就会每次增加 \(p_i\) ,直至生命值非负。只有在攻击结束后且当生命值 恰好 为 \(0\) 时它才会死去。
  • 游戏开始时玩家拥有 \(m\) 把攻击力已知的剑,每次面对巨龙时,玩家只能选择一把剑,当杀死巨龙后这把剑就会消失,但作为奖励,玩家会获得全新的一把剑。

小 D 觉得这款游戏十分无聊,但最快通关的玩家可以获得 ION2018 的参赛资格,于是小 D 决定写一个笨笨的机器人帮她通关这款游戏,她写的机器人遵循以下规则:

  • 每次面对巨龙时,机器人会选择当前拥有的,攻击力不高于巨龙初始生命值中攻击力最大的一把剑作为武器。如果没有这样的剑,则选择 攻击力最低 的一把剑作为武器。
  • 机器人面对每条巨龙,它都会使用上一步中选择的剑攻击巨龙固定的 \(x\) 次,使巨龙的生命值减少 \(x \times ATK\) 。
  • 之后,巨龙会不断使用恢复能力,每次恢复 \(p_i\) 生命值。若在使用恢复能力前或某一次恢复后其生命值为 \(0\) ,则巨龙死亡,玩家通过本关。

那么显然机器人的攻击次数是决定能否最快通关这款游戏的关键。小 D 现在得知了每条巨龙的所有属性,她想考考你,你知道应该将机器人的攻击次数 \(x\) 设置为多少,才能用最少的攻击次数通关游戏吗?

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

思路

part 1

首先,我们很容易能够知道。其实每一只龙 \(i\) 一定有对应的用来砍它的剑。为什么,这里简单证明一下。第一步,注意题目中说的:

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

这就说明,每一次选剑的策略是一定的,那么每一只龙就一定有一把对应用来砍它的剑 \(b_i\)。即使后面加入新的剑也是有同样的。相当于我们可以收先处理出每一只龙所对应的剑。这里的实现必须是 \(O(n \log n)\) 的。考虑使用平衡树或者直接使用 multiset显然 multiset 会好写一些。

这一部分的处理代码就长这样(其中 s 就是 multiset):

	for(int i=1;i<=n;i++){
		auto place=s.upper_bound(a[i]);
		if(place!=s.begin()){
			place--;
		}
		b[i]=*place;
		s.erase(place);
		s.insert(t[i]);
		maxn=max(maxn,(a[i]-1)/b[i]+1);
	}

part 2

然后我们进入核心阶段。由于上面我们找到了每一只龙对应的剑 \(b_i\) 且我们知道 我们要固定攻击 \(k\) 次。那么就有我们造成的总伤害为 \(b_i \times k\)。又因为龙的初始生命值为 \(a_i\)。那么易得满足让这一只龙去世的满足条件的 \(x\) 也一定满足下面这个式子:

\[b_i \times x \equiv a_i \pmod {q_i} \]

那么多条龙也是一样的,我们就可以列出方程组:

\[\left\{ \begin{array}{l} \text{$b_1 \times x \equiv a_1 \pmod {q_1}$} \\ \text{$b_2 \times x \equiv a_2 \pmod {q_2}$} \\ \text{......} \\ \text{$b_n \times x \equiv a_n \pmod {q_n}$} \end{array} \right. \]

part 3

现在已经能看出来,这题明显可以用 ExCrt 求解。但是这里还有个系数 \(b_i\)。应该怎么办。其实比较好处理。这里笔者参考了 emptysetvvvv 大佬的 博客。这篇文章大致是这样说的:我们先假设我们已经得到了前 \(i - 1\) 个方程的解,记为 \(res\),且假设有 LCM 等于 \(\mathrm{lcm}(p_1,p_2 ...... p_{i-1})\)。对于前 \(i\) 个方程,我们想得到它们的解就是想找到一个 \(x\) 满足下面这式子:

\[b_i \times (res + LCM \times x) \equiv a_i \pmod {q_i} \]

为什么?首先我们可以知道 \(LCM \times x + res\) 为前 \(i-1\) 的通解。那么上面这个式子就很显然了。

然后,我们把上面那个式子移项。就能发现这个式子其实就等价于:

\[(b_i \times LCM) \times x + p_i \times y = a_i - b_i \times ans \]

于是直接扩展欧几里得求解。注意计算过程会爆 long long。建议直接使用 __int128

AC code

#include<bits/stdc++.h>
#define int __int128
using namespace std;
namespace WYL{
	const int N=1e5+10;
	multiset<int> s;
	int T,n,m,a[N],b[N],p[N],t[N],maxn=0;
	inline int read(){
		int x=0,f=1;
		char ch=getchar();
		while(ch<'0'||ch>'9'){
			if(ch=='-')
				f=-1;
			ch=getchar();
		}
		while(ch>='0' && ch<='9')
			x=x*10+ch-'0',ch=getchar();
		return x*f;
	}
	inline void Write(int x){
		if(x<0)
			putchar('-'),x=-x;
		if(x>9)
			Write(x/10);
		putchar(x%10+'0');
		return;
	}
	int gsc(int a,int b,int mod){
		int ans=0;
		while(b){
			if(b&1){
				ans=(ans+a)%mod;
			}
			a=(a+a)%mod;
			b/=2;
		}
		return ans;
	}
	void exgcd(int a,int b,int &x,int &y,int &gcd) {
		if(!b){
			x=1;
			y=0;
			gcd=a;	
		}else{
			exgcd(b,a%b,y,x,gcd);
			y-=(a/b)*x;	
		}
		return;
	}
	int solve(){
		int ans=0,lcm=1,x,y,Gcd,yi,er,san;
		for(int i=1;i<=n;i++){
//			yi=gsc(b[i],lcm,p[i]);
			yi=b[i]*lcm%p[i];
//			cout<<"pass1"<<endl;
			er=p[i];
			san=(a[i]-b[i]*ans%p[i]+p[i])%p[i];
//			cout<<"pass2"<<endl;
			exgcd(yi,er,x,y,Gcd);
//			cout<<"pass3"<<endl;
			x=(x%er+er)%er;
			if(san%Gcd){
				return -1;
			}
//			lcm=lcm*(er/Gcd);
			ans=(ans+(san/Gcd)*x%(er/Gcd)*lcm%(lcm*=er/Gcd))%lcm;
//			ans=(ans+gsc(san/Gcd,x,er/Gcd)*lcm%(lcm*er/Gcd))%lcm;
		}
		if(ans<maxn){
			ans=ans+((maxn-ans-1)/lcm+1)*lcm;
		}
		return ans;
	}
	int main(){
		T=read();
		while(T--){
			s.clear();
			n=read();
			m=read();
			maxn=0;
			for(int i=1;i<=n;i++){
				a[i]=read();
			}
			for(int i=1;i<=n;i++){
				p[i]=read();
			}
			for(int i=1;i<=n;i++){
				t[i]=read();
			}
			for(int i=1;i<=m;i++){
				int opt;
				opt=read();
				s.insert(opt);
			}
//			cout<<"pass"<<endl;
			for(int i=1;i<=n;i++){
				auto place=s.upper_bound(a[i]);
				if(place!=s.begin()){
					place--;
				}
				b[i]=*place;
				s.erase(place);
				s.insert(t[i]);
				maxn=max(maxn,(a[i]-1)/b[i]+1);
			}
//			cout<<ExCRT()<<endl;
			Write(solve());
			puts("");
		}
		return 0;
	}
}
signed main(){
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
//	freopen("dragon.in","r",stdin);
//	freopen("dragon.out","w",stdout);
	WYL::main();
	return 0;
}

标签:生命,巨龙,NOI,攻击力,int,times,2018,ans,屠龙
From: https://www.cnblogs.com/OluMel/p/18502096

相关文章

  • P1085 [NOIP2004 普及组] 不高兴的津津 难点:如何按要求实现打印最生气的天数.py
    """anger=0day=0foriinrange(7):inclass,extra=input(map(int,input().split()))anger=inclass+extraday+=1"""#将anger数组的大小排序,输出anger最大的那一天,但我无法将anger和day连接起来排序#解决办法是用max_anger和angriest_day两个变量,在七天的......
  • 洛谷 P1002 [NOIP2002 普及组] 过河卒
    你好,我是gwg725。洛谷账号:大号小号欢迎与我讨论。:)题目描述:棋盘上A点有一个过河卒,需要走到目标B点。卒行走的规则:可以向下、或者向右。同时在棋盘上的某一点有一个对方的马(如C点),该马所在的点和所有跳跃一步可达的点称为对方马的控制点,如图3-1中的C点和P1,……,P8,卒不能通过......
  • [赛记] 多校A层冲刺NOIP2024模拟赛11 && 12
    冒泡排序100pts比较显然的签到题(好久没这么水过了);考虑这个错的冒泡排序,手模一下即可发现这个$+k$有点像以前做过的同余系中求和的问题,于是这个题同理,用set维护每个同余系的排名,最后按顺序输出即可;对于正确性,相当于每次$+k$,则就相当于在一个同余系中排序;时间复杂度:$......
  • 23~24 炼石计划 NOIP 练习题部分题解
    目录目录第1组JOISC2017火车旅行IOI2018会议CF1558FStrangeSortAPIO2018新家CTSC2017密钥CF1748EYetAnotherArrayCountingProblem第2组NOI2016区间LOJ552MIN&MAXIJOISC2023合唱LOJ542序列划分LOJ560Menci的序列P8978中位数第3组USACO20FEBHelpYourse......
  • 梦熊 NOIP 13连测 #13
    赛时100+75+30+0A.求对于区间\([l,r]\)的答案,转换成$ans(R)-ans(L-1)\(,\)ans(i)$表示小于等于\(i\)的元素个数。对于\(x\),二分小于等于\(x\)的个数,可以直接对于二分的这个数q次操作判断,因为如果它可以小于它的一定也可以。时间复杂度\(O(Q\logn)\)B.75pt......
  • 多校A层冲刺NOIP2024模拟赛12
    多校A层冲刺NOIP2024模拟赛12\(T1\)A.Alice和璀璨花\(65pts/65pts/65pts\)部分分测试点\(1\sim10\):设\(f_{i,j}\)表示前\(i\)位中生长趋势子序列长度为\(j\)时的末尾最小元素,然后进行暴力转移。测试点\(11\sim13\):观察到至多选择\(\left\lceil\log_......
  • 『模拟赛』多校A层冲刺NOIP2024模拟赛12
    Rank挂了不少,还行A.Alice和璀璨花签。一眼最长上升子序列,昨天在AT专题里刚见过,不过赛时没想到离散化之后树状数组,所以打的动态开点,结果细节挂了30pts。和最长上升子序列思路基本一致,直接区间查询\([1,a_i-1]\)的最大值,然后在\(a_i\timesb_{f_i}\)插入\(f_i\)......
  • 洛谷 P2680 [NOIP2015 提高组] 运输计划 做题记录
    首先题目要求最大的最小,我们二分答案,对于每个答案,我们筛出比它长的路径,找到它们最长的公共边,删掉后验证正确性即可。找公共边可以用树上差分来做,时间复杂度\(O(m\logn\logV)\),其中\(V\)是二分区间大小。你会发现你挂了一堆点,让我们来卡常:首先预处理出所有节点的\(dfn\),每......
  • P1040 [NOIP2003 提高组] 加分二叉树
    P1040[NOIP2003提高组]加分二叉树题目描述设一个\(n\)个节点的二叉树\(\text{tree}\)的中序遍历为\((1,2,3,\ldots,n)\),其中数字\(1,2,3,\ldots,n\)为节点编号。每个节点都有一个分数(均为正整数),记第\(i\)个节点的分数为\(d_i\),\(\text{tree}\)及它的每个子树都有一......
  • Cinemachine系列——Noise&Basic Multi Channel Perlin
    在Cinemachine相机的游戏对象中使用基本多通道柏林噪声组件,以通过柏林噪声运动模拟相机抖动。柏林噪声是一种计算伪随机运动并具有自然行为的技术。简单来说,基本多通道柏林噪声组件应用了一个噪声配置资产,用于定义噪声随时间变化的行为。Cinemachine自带了一些噪声配置资产,你可以......