zoukankan      html  css  js  c++  java
  • 牛客NOIP2020模拟

    牛牛的RPG游戏

    题意: (n imes m) 的网格,需要从 ((1, 1)) 移动到 ((n, m)),移动一格会得到 (buff) 分,到达一格可以选择触发事件,获得 (val_{i, j}) 分,并替换自身 (buff)(buff_{i, j}) 。走最短路的情况下,最大化总得分。

    写出转移方程:

    [dp_{i, j}=max{dp_{p, q}+buff_{p, q} imes(i + j - p - q)} + val_{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) 损失的血量为

    [egin{align} A_{x, y} &= r_xatk_x+r_yatk_y-(r_x+r_y)def_0-r_yd_x\ A_{y, x} &= r_xatk_x+r_yatk_y-(r_x+r_y)def_0-r_xd_y end{align} ]

    (A_{x, y} < A_{y, x}),那么 (r_xd_y<r_yd_x),即 (frac{d_x}{r_x}>frac{d_y}{r_y})

    那么我们可以知道整棵树内 (frac dr) 最大的一定会在它父亲被杀死后立即被杀死。

    所以每次取出最大的和它父亲合并成一个。

    [egin{align} A_{x, y, z} &= r_xatk_x+r_yatk_y+r_zatk_z-(r_x+r_y+r_z)def_0-r_yd_x-r_z(d_x+d_y)\ A_{z, x, y} &= r_xatk_x+r_yatk_y+r_zatk_z-(r_x+r_y+r_z)def_0-r_yd_x-(r_x+r_y)d_z end{align} ]

    每个怪重新分配属性 ((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;
    }
    
    
  • 相关阅读:
    dialog弹层背景overlayer实现的方式
    省略号 对单行 多行的css
    js == 判断
    jquery选择伪元素属性的方法
    psp0级报告
    计算器
    Java学习笔记
    第一个PSP0级
    设计简单登录界面(Java web)
    登录界面及其功能的设计
  • 原文地址:https://www.cnblogs.com/HolyK/p/13921536.html
Copyright © 2011-2022 走看看