牛牛的RPG游戏
题意: (n imes m) 的网格,需要从 ((1, 1)) 移动到 ((n, m)),移动一格会得到 (buff) 分,到达一格可以选择触发事件,获得 (val_{i, j}) 分,并替换自身 (buff) 为 (buff_{i, j}) 。走最短路的情况下,最大化总得分。
写出转移方程:
令 (L_{p, q}(x) = buff_{p,q}x+dp_{p, q} - (p + q)buff_{p, q}),要求最大化 (L_{p, q}(i+j)),可以用李超线段树维护。
另外有条件 (p le i, q le j),相当于二维偏序,可用CDQ分治或树状数组维护。
#include <bits/stdc++.h>
template <class T>
inline void readInt(T &w) {
char c, p = 0;
while (!isdigit(c = getchar())) p = c == '-';
for (w = c & 15; isdigit(c = getchar());) w = w * 10 + (c & 15);
if (p) w = -w;
}
template <class T, class... U>
inline void readInt(T &w, U &... a) { readInt(w), readInt(a...); }
template <class T, class U>
inline bool smin(T &x, const U &y) { return y < x ? x = y, 1 : 0; }
template <class T, class U>
inline bool smax(T &x, const U &y) { return x < y ? x = y, 1 : 0; }
typedef long long LL;
typedef std::pair<int, int> PII;
constexpr int N(1e5 + 5);
int mem_pool[N * 2], *m_ptr = mem_pool;
int n, m, *buff[N], *val[N];
struct Line {
int k, b;
inline int func(int x) { return k * x + b; }
};
struct Node {
Node *ls, *rs;
Line v;
} t[N * 30], *t_ptr = t;
void ins(Node *&o, int l, int r, Line x) {
if (!o) {
o = t_ptr++;
o->v = x;
return;
}
int mid = l + r >> 1;
bool lf = o->v.func(l) < x.func(l);
bool ri = o->v.func(r) < x.func(r);
bool mi = o->v.func(mid) < x.func(mid);
if (mi) std::swap(o->v, x);
if (l == r || lf == ri) return;
lf != mi ? ins(o->ls, l, mid, x) : ins(o->rs, mid + 1, r, x);
}
int ask(Node *o, int l, int r, int x) {
if (!o) return 0;
int mid = l + r >> 1;
if (x == mid) return o->v.func(x);
return std::max(o->v.func(x), x <= mid ? ask(o->ls, l, mid, x) : ask(o->rs, mid + 1, r, x));
}
Node *c[N];
inline void add(int p, Line x) {
for (p++; p <= m; p += p & -p) ins(c[p], 0, n + m - 2, x);
}
inline int ask(int p, int x) {
int ans = 0;
for (p++; p; p -= p & -p) smax(ans, ask(c[p], 0, n + m - 2, x));
return ans;
}
int main() {
readInt(n, m);
if (n == 1 && m == 1) {
puts("0");
return 0;
}
for (int i = 0; i < n; i++) {
buff[i] = m_ptr;
for (int j = 0; j < m; j++)
readInt(*m_ptr++);
}
for (int i = 0; i < n; i++) {
val[i] = m_ptr;
for (int j = 0; j < m; j++)
readInt(*m_ptr++);
}
int ans = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
int nw = ask(j, i + j) + val[i][j];
smax(ans, nw + buff[i][j] * (n - 1 - i + m - 1 - j));
add(j, {buff[i][j], nw - (i + j) * buff[i][j]});
}
}
std::cout << ans;
return 0;
}
移动
题意:(N) 道闸门在 (1dots N) 位置,每道闸门有一些关闭的时间区间,移动一步需要一个单位时间,求 (0) 时刻出发从 (0) 到 (N+1) 的最短时间。
((t, p, r)) 表示 (t) 时刻在第 (p) 道闸门,该闸门最近将在 (r) 时刻关闭。
初始状态 ((0, 0, inf)),结束状态 ((ans, N+1, inf))
考虑前进或后退时,如果目的地门开着,就直接走过去,否则等它开了再走过去。
状态数不多,用类似dijkstra的方式拓展。
(这实现有亿点巧妙)
#include <bits/stdc++.h>
template <class T>
inline void readInt(T &w) {
char c, p = 0;
while (!isdigit(c = getchar())) p = c == '-';
for (w = c & 15; isdigit(c = getchar()); w = w * 10 + (c & 15));
if (p) w = -w;
}
template <class T, class... U>
inline void readInt(T &w, U &... a) { readInt(w), readInt(a...); }
template <class T, class U>
inline bool smin(T &x, const U &y) { return y < x ? x = y, 1 : 0; }
template <class T, class U>
inline bool smax(T &x, const U &y) { return x < y ? x = y, 1 : 0; }
typedef long long LL;
typedef std::pair<int, int> PII;
constexpr int N(1e5 + 5);
constexpr int D[] = {-1, 1};
struct State {
int t, p, r;
inline bool operator<(const State &rhs) const {
return t > rhs.t;
}
};
std::priority_queue<State> q;
int n, m;
std::vector<PII> a[N];
int main() {
readInt(n, m);
while (m--) {
int p, x, y; readInt(p, x, y);
a[p].emplace_back(x, y);
}
for (int i = 1; i <= n; i++) {
a[i].emplace_back(2e9, 2e9);
std::sort(a[i].begin(), a[i].end(), std::greater<PII>());
a[i].emplace_back(0, 0);
}
q.push({0, 0, (int)2e9});
while (!q.empty()) {
State z = q.top(); q.pop();
if (z.p == n) return printf("%d", z.t + 1), 0;
for (int d: D) {
int y = z.p + d;
if (y < 1 || y > n) continue;
while (a[y].back().second < z.r) {
State v = { std::max(z.t + 1, a[y].back().second + 1), y, a[y][a[y].size() - 2].first };
if (v.t < v.r) q.push(v);
a[y].pop_back();
}
}
}
puts("-1");
return 0;
}
牛半仙的魔塔(增强版)
一棵以 (1) 为根的树上,除了根以外的每个点都有一个怪物,你和怪物都有攻、防、血三个属性,战斗时你先手。打死怪之后你会增加 (d_i) 点防御力,求打完怪的最大血量。
经典贪心题,类似于 color a tree。
你的攻击、怪物的防御、血量都是一定的,所以可以算出回合数 (r_i)。
损失的血量 (A_i = r_i(atk_i - def_0))
连续打 (x, y) 损失的血量为
若 (A_{x, y} < A_{y, x}),那么 (r_xd_y<r_yd_x),即 (frac{d_x}{r_x}>frac{d_y}{r_y})。
那么我们可以知道整棵树内 (frac dr) 最大的一定会在它父亲被杀死后立即被杀死。
所以每次取出最大的和它父亲合并成一个。
每个怪重新分配属性 ((r_iatk_i, r_i, d_i)),合并两个怪时各个属性相加并产生 (r_yd_x) 代价。
#include <bits/stdc++.h>
#ifdef LOCAL
#define dbg(args...) do std::cerr << " 33[32;1m" << #args << " -> ", err(args); while (0)
#else
#define dbg(...)
#endif
inline void err() { std::cerr << " 33[39;0m
"; }
template<class T, class... U>
inline void err(const T &x, const U &... a) { std::cerr << x << ' '; err(a...); }
template <class T>
inline void readInt(T &w) {
char c, p = 0;
while (!isdigit(c = getchar())) p = c == '-';
for (w = c & 15; isdigit(c = getchar());) w = w * 10 + (c & 15);
if (p) w = -w;
}
template <class T, class... U>
inline void readInt(T &w, U &... a) { readInt(w), readInt(a...); }
template <class T, class U>
inline bool smin(T &x, const U &y) { return y < x ? x = y, 1 : 0; }
template <class T, class U>
inline bool smax(T &x, const U &y) { return x < y ? x = y, 1 : 0; }
typedef long long LL;
typedef std::pair<int, int> PII;
constexpr int N(1e5 + 5);
int n, fa[N];
std::vector<int> g[N];
void dfs(int x) {
for (int y: g[x])
if (y != fa[x])
fa[y] = x, dfs(y);
}
struct Node {
LL a, r;
int d, id;
inline bool operator<(const Node &rhs) const {
return d * rhs.r < rhs.d * r;
}
} val[N];
std::priority_queue<Node> q;
namespace UFS {
int fa[N];
inline int find(int x) {
return fa[x] ? fa[x] = find(fa[x]) : x;
}
}
int main() {
readInt(n);
for (int i = 1, x, y; i < n; i++) {
readInt(x, y);
g[x].emplace_back(y), g[y].emplace_back(x);
}
dfs(1);
LL ans; int atk0, def0;
readInt(ans, atk0, def0);
for (int i = 2; i <= n; i++) {
LL hp; int atk, def;
readInt(hp, atk, def);
if (atk0 <= def) return puts("-1"), 0;
Node &v = val[i];
v.r = (hp - 1) / (atk0 - def);
v.a = atk * v.r;
readInt(v.d);
v.id = i;
q.push(v);
}
while (!q.empty()) {
Node v = q.top(); q.pop();
if (v.d != val[v.id].d) continue;
int f = UFS::find(fa[v.id]);
ans += v.r * val[f].d;
val[f].a += v.a;
val[f].d += v.d;
val[f].r += v.r;
UFS::fa[v.id] = f;
if (f > 1) q.push(val[f]);
dbg(v.id);
}
ans -= val[1].a;
std::cout << (ans <= 0 ? -1 : ans) << "
";
return 0;
}