## [「NOI2018」屠龙勇士](https://loj.ac/problem/2721)

题目描述

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

游戏的目标是按照编号\(1-n\)顺序杀掉\(n\) 条巨龙,每条巨龙拥有一个初始的生命 值ai 。同时每条巨龙拥有恢复能力,当其使用恢复能力时,它的生命值就会每 次增加 \(p_i\) ,直至生命值非负。只有在攻击结束后且当生命值恰好为 \(0\) 时它才会 死去。

游戏开始时玩家拥有\(m\)把攻击力已知的剑,每次面对巨龙时,玩家只能选择一 把剑,当杀死巨龙后这把剑就会消失,但作为奖励,玩家会获得全新的一把剑。 小\(D\) 觉得这款游戏十分无聊,但最快通关的玩家可以获得\(ION2018\) 的参赛资格, 于是小\(D\) 决定写一个笨笨的机器人帮她通关这款游戏,她写的机器人遵循以下规则:

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

机器人面对每条巨龙,它都会使用上一步中选择的剑攻击巨龙固定的\(x\)次,使巨龙的生命值减少 \(x \times ATK_x\)

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

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

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

### 解题思路 :

观察发现,面对每一条龙时的攻击力 \(atk_i\) 是固定的,不妨用一个 \(multiset\) 求出.

考虑之后的部分,第 \(i\) 条龙要被打死当且仅当 \((a_i - x \times atk_i) \ |\ p_i\) 且 \(a_i \leq x \times atk_i\)

前一部分约束可以转化为 \(x \times atk_i \equiv ai \ ( \mod p_i\ )\) 可以直接用 \(Exgcd\) 求解出 \(x\)

注意:设 \(g = gcd(atk_i, \ p_i)\) 此时解出的 \(x\) 是在 \((\mod \frac{p_i}{g} \ )\) 意义下成立的, 而不是 \(p_i\)

\(\because x \times atk_i \equiv ai \ ( \mod p_i\ ) \rightarrow atk_ix \ + \ p_iy = a_i \rightarrow atk_i\frac{x}{g} + p_i\frac{y}{g} = \frac{a_i}{g}\)

用上述方法对于所有 \((atk_i, p_i)\) 都能求出一个同余方程的解满足 \(x \equiv d_i \ (\mod p'_i\ )\)

因为要满足对于所有巨龙都要用同样的 \(x\) 杀死,所以需要用 \(Excrt\) 合并这些同余方程组

假设当前有两个方程组 $$ \begin{cases} x \equiv a \ (\mod p_1g\ ) \ x \equiv b \ (\mod p_2g \ ) \end{cases}$$

​ \(g = gcd(p_1g, \ p_2g)\)

设 \(x = a \ +\ k_1p_1g\) ,代入第二个方程得 $ a + k_1p1_g + k_2p_2g = b$

移项得 :$k_1p_1+k_2p_2 = \frac{b-a}{g} $ 再次用 \(Exgcd\) 求解出 \(k1\) 即可得到 \(x\) 在 \((\mod \frac{p_1p_2}{g}\ )\) 的解了

合并出方程组的解后还需要满足第二个限制条件 \(a_i \leq x \times atk_i\)

可以把 \(x\) 代入到原有的数组里,判断是否满足限制,如果不满足就变成第一个比它大的解

注意:上述求类似 \(ax + by = c\) 的解的过程中,如果任意一步不满足 \(gcd(a, b) \ | \ c\) ,则整个方程组不可能有解,应该输出 \(-1\)

部分乘法过程中可能出现爆 \(ll\) 的情况,用快速乘来代替即可.


/*program by mangoyang*/
#include<bits/stdc++.h>
#define inf (0x7f7f7f7f)
#define Max(a, b) ((a) > (b) ? (a) : (b))
#define Min(a, b) ((a) < (b) ? (a) : (b))
typedef long long ll;
using namespace std;
template <class T>
inline void read(T &x){
int f = 0, ch = 0; x = 0;
for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1;
for(; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
if(f) x = -x;
}
#define int ll
#define fi first
#define se second
#define N (300005)
multiset<int> S;
int a[N], b[N], c[N], p[N], atk[N], n, m;
struct Node{ int x, m; } d[N];
namespace Crt{
int Mod;
inline int gcd(int a, int b){ return b ? gcd(b, a % b) : a; }
inline int Mult(int a, int b){
int ans = 0ll;
for(; b; b >>= 1, a = (a + a) % Mod)
if(b & 1) ans = (ans + a) % Mod;
return ans;
}
inline void exgcd(int a, int b, int &x, int &y){
if(!b) return (void) (x = 1ll, y = 0ll);
exgcd(b, a % b, y, x), y -= a / b * x;
}
inline pair<int, int> get_exgcd(int a, int b, int c){
int g = gcd(a, b);
if(c % g) return make_pair(-1ll, -1ll);
int x, y; exgcd(a, b, x, y);
x = (x + Mod) % Mod, y = (y + Mod) % Mod;
x = Mult(x, c / g), y = Mult(y, c / g);
return make_pair(x, y);
}
inline Node merge(Node A, Node B){
if(A.x > B.x) swap(A, B);
int g = gcd(A.m, B.m); Mod = A.m / g * B.m;
pair<int, int> now = get_exgcd(A.m, B.m, B.x - A.x);
if(now.fi == -1) return (Node){-1ll, -1ll};
return (Node){(A.x + Mult(A.m, now.fi)) % Mod, Mod};
}
inline Node solve(Node *A){
if(n == 1) return A[1];
Node now = merge(A[1], A[2]);
for(int i = 3; i <= n; now = merge(now, A[i]), i++)
if(now.x == -1 && now.m == -1) return now;
return now;
}
}
inline void init(){
read(n), read(m);
for(int i = 1; i <= n; i++) read(a[i]);
for(int i = 1; i <= n; i++) read(p[i]);
for(int i = 1; i <= n; i++) read(b[i]);
for(int i = 1; i <= m; i++) read(c[i]);
}
inline void prepare(){
S.clear();
multiset<int>::iterator q;
for(int i = 1; i <= m; i++) S.insert(c[i]);
for(int i = 1; i <= n; i++){
q = S.upper_bound(a[i]);
if(q != S.begin()) q--;
atk[i] = *(q), S.erase(q), S.insert(b[i]);
}
}
inline void solve(){
init(), prepare();
for(int i = 1; i <= n; i++){
Crt::Mod = p[i];
pair<int, int> now = Crt::get_exgcd(atk[i], p[i], a[i]);
if(now.fi == -1) return (void) puts("-1");
d[i] = (Node){now.fi, p[i] / Crt::gcd(atk[i], p[i])};
}
Node res = Crt::solve(d);
if(res.x == -1 && res.m == -1) return (void) puts("-1");
int x = res.x, m = res.m; x %= m;
for(int i = 1; i <= n; i++){
int ned = a[i] % atk[i] == 0 ? a[i] / atk[i] : a[i] / atk[i] + 1;
if(x < ned){
int tmp = ned - x;
if(tmp % m == 0) x += tmp / m; else x += tmp / m + 1;
}
}
printf("%lld\n", x);
}
main(){
int T; read(T); while(T--) solve();
return 0;
}

「NOI2018」屠龙勇士的更多相关文章

  1. 「NOI2018」屠龙勇士(EXCRT)

    「NOI2018」屠龙勇士(EXCRT) 终于把传说中 \(NOI2018D2\) 的签到题写掉了... 开始我还没读懂题目...而且这题细节巨麻烦...(可能对我而言) 首先我们要转换一下,每次的 ...

  2. LOJ #2721. 「NOI2018」屠龙勇士(set + exgcd)

    题意 LOJ #2721. 「NOI2018」屠龙勇士 题解 首先假设每条龙都可以打死,每次拿到的剑攻击力为 \(ATK\) . 这个需要支持每次插入一个数,查找比一个 \(\le\) 数最大的数(或 ...

  3. loj#2721. 「NOI2018」屠龙勇士

    题目链接 loj#2721. 「NOI2018」屠龙勇士 题解 首先可以列出线性方程组 方程组转化为在模p意义下的同余方程 因为不保证pp 互素,考虑扩展中国剩余定理合并 方程组是带系数的,我们要做的 ...

  4. POJ1061 青蛙的约会 和 LOJ2721 「NOI2018」屠龙勇士

    青蛙的约会 Language:Default 青蛙的约会 Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 133470 Accep ...

  5. 「NOI2018」屠龙勇士 解题报告

    「NOI2018」屠龙勇士 首先对于每个龙用哪个剑砍,我们可以用set随便模拟一下得到. 然后求出拿这个剑砍这条龙的答案 \[ atk_ix-p_iy=a_i \] 其中\(atk_i\)是砍第\(i ...

  6. 「NOI2018」屠龙勇士(CRT)

    /* 首先杀每条龙用到的刀是能够确定的, 然后我们便得到了许多形如 ai - x * atki | pi的方程 而且限制了x的最小值 那么exgcd解出来就好了 之后就是扩展crt合并了 因为全T调了 ...

  7. LOJ 2721 「NOI2018」屠龙勇士——扩展中国剩余定理

    题目:https://loj.ac/problem/2721 1.注意别一输入 p[ i ] 就 a[ i ] %= p[ i ] ,因为在 multiset 里找的时候还需要真实值. 2.注意用 m ...

  8. 「NOI2018」你的名字

    「NOI2018」你的名字 题目描述 小A 被选为了\(ION2018\) 的出题人,他精心准备了一道质量十分高的题目,且已经 把除了题目命名以外的工作都做好了. 由于\(ION\) 已经举办了很多届 ...

  9. loj#2718. 「NOI2018」归程

    题目链接 loj#2718. 「NOI2018」归程 题解 按照高度做克鲁斯卡尔重构树 那么对于询问倍增找到当前点能到达的高度最小可行点,该点的子树就是能到达的联通快,维护子树中到1节点的最短距离 s ...

随机推荐

  1. Linux系统网络基础知识及配置

    一:DNS(domain name system)简介 DNS(Domain Name System,域名系统),因特网上作为域名和IP地址相互映射的一个分布式数据库,能够使用户更方便的访问互联网,而 ...

  2. centos 挂载数据盘

    第一.检查硬盘设备是否有数据盘 fdisk -l 第二.数据硬盘分区 fdisk /dev/vdb 第三.ext3格式化分区 mkfs.ext3 /dev/vdb1 第四.挂载新分区 A - 新建目录 ...

  3. CSS 竖线颜色渐变

    <!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"& ...

  4. Coursera在线学习---第八节.K-means聚类算法与主成分分析(PCA)

    一.K-means聚类中心初始化问题. 1)随机初始化各个簇类的中心,进行迭代,直到收敛,并计算代价函数J. 如果k=2~10,可以进行上述步骤100次,并分别计算代价函数J,选取J值最小的一种聚类情 ...

  5. Python3 面向对象编程

    小案例: #!/usr/bin/env python # _*_ coding:utf-8 _*_ # Author:Bert import sys class Role(object): n=&qu ...

  6. Django【设计】可插拔的插件方式实现

    需求: 在CMDB系统中,我们需要对资产进行采集和资产入库,包括serverBasic.disk.memory.nic信息等,客户端需要采集这些硬件的信息,服务端则负责资产入库,但是需要采集的硬件并不 ...

  7. System V共享内存介绍

    (一)简单概念 共享内存作为一种进程间通信的方式,其相较于其他进程间通信方式而言最大的优点就是数据传输速率快.其内部实现的方式采用了Linux进程地址空间中的mmap文件映射区,将文件内容直接映射到各 ...

  8. tenda t402 家庭版 有线路由器

    使用快速向导: adsl(拨号)+用户名+密码 路由器后DMZ主机设置简单图解:http://wenku.baidu.com/view/94b9f0768e9951e79b8927ce.html  可 ...

  9. Android IPC

    1. 什么是Android IPC IPC:inter-process Commnication跨进程的通信,多进程之间的通信,不同的操作系统有不同的通信方式,Android继承自Linux,但其IP ...

  10. Leetcode 之Binary Tree Inorder Traversal(43)

    树的中序遍历.先不断压入左结点至末尾,再访问,再压入右结点.注意和先序遍历的比较 vector<int> inorderTraversal(TreeNode *root) { vector ...