CodeForces 1023G. Pisces

链接:

link

题意:

有一棵 n n 个点的树,有边权,进行了 k 次观察,每次观察是在第 di d i 天在 pi p i 点至少有 fi f i 条鱼,鱼每天可以走一个单位,问最少有多少条鱼。

题解:

考虑 Dilworth 定理,求最长反链就行了。

一个结论是,考虑子树 x x 中的一个集合形成反链当且仅当:

  • 每个子树内部是反链。
  • 存在一个时刻 t 使得不存在鱼能够在 t t 时刻到达 x ,也不能从 x x t 时刻出发到达任何鱼。

    然后就可以用一个很基础的树形DP问题了,用数据结构维护DP差分后的值,合并时可以启发式合并,而产生的冲突可以用一个set来维护。

    具体代码怎么写就各凭本事了

    代码:

    #include <bits/stdc++.h>
    
    using namespace std;
    
    const int N = 100005;
    
    vector<pair<int, int>> adj[N], fish[N];
    int n, m, now, depth[N];
    
    struct node_t {
      int x, d, v;
    
      node_t(int x, int d, int v, int t = 0):x(x - d * t), d(d), v(v) {
      }
    
      bool operator < (const node_t &b) const {
        int p = x + now * d, q = b.x + now * b.d;
        return p == q ? d < b.d : p < q;
      }
    };
    
    struct collision_t {
      node_t x, y;
      int when;
    
      collision_t(node_t x, node_t y):x(x), y(y) {
        when = y.x - x.x >> 1;
      }
    
      bool operator < (const collision_t &b) const {
        if (when != b.when) {
          return when < b.when;
        } else {
          return make_tuple(x.x, x.v, y.x, y.v) < make_tuple(b.x.x, b.x.v, b.y.x, b.y.v);
        }
      }
    };
    
    struct info_t {
      set<collision_t> collisions;
      set<node_t> nodes;
    
      void insert_collision(node_t x, node_t y) {
        if (x.d > 0 && y.d < 0) {
          collisions.insert(collision_t(x, y));
        }
      }
    
      void erase_collision(node_t x, node_t y) {
        if (x.d > 0 && y.d < 0) {
          collisions.erase(collision_t(x, y));
        }
      }
    
      void insert(node_t x) {
        if (!x.v) {
          return;
        }
        if (nodes.find(x) != nodes.end()) {
          x.v += nodes.find(x)->v;
          erase(*nodes.find(x));
        }
        auto it = nodes.insert(x).first, next = it;
        ++next;
        if (it != nodes.begin()) {
          auto prev = it;
          --prev;
          if (next != nodes.end()) {
            erase_collision(*prev, *next);
          }
          insert_collision(*prev, x);
        }
        if (next != nodes.end()) {
          insert_collision(x, *next);
        }
      }
    
      void erase(node_t x) {
        auto it = nodes.find(x), next = it;
        ++next;
        if (next != nodes.end()) {
          erase_collision(x, *next);
        }
        if (it != nodes.begin()) {
          auto prev = it;
          --prev;
          erase_collision(*prev, x);
          if (next != nodes.end()) {
            insert_collision(*prev, *next);
          }
        }
        nodes.erase(it);
      }
    
      int reset(int x, int y) {
        node_t l(x + 1, -1, y, now), r(x - 1, 1, y, now);
        return max(0, y - max(nodes.find(l) == nodes.end() ? 0 : nodes.find(l)->v, nodes.find(r) == nodes.end() ? 0 : nodes.find(r)->v));
      }
    
      node_t cancel(node_t x, node_t y) {
        if (x.v > y.v) {
          return node_t(x.x, x.d, x.v - y.v);
        } else {
          return node_t(y.x, y.d, y.v - x.v);
        }
      }
    
      void collide(collision_t x) {
        erase(x.x);
        erase(x.y);
        insert(cancel(x.x, x.y));
      }
    
      void maintain(int x) {
        while (!collisions.empty() && collisions.begin()->when <= x) {
          collide(*collisions.begin());
        }
      }
    
      void merge(info_t &b) {
        if (nodes.size() < b.nodes.size()) {
          swap(nodes, b.nodes);
          swap(collisions, b.collisions);
        }
        for (auto x : b.nodes) {
          insert(x);
        }
      }
    
      int query() {
        int answer = 0;
        for (auto x : nodes) {
          if (x.d > 0) {
            answer += x.v;
          }
        }
        return answer;
      }
    } info[N];
    
    void dfs(int x, int parent) {
      for (auto e : adj[x]) {
        int y = e.first, w = e.second;
        if (y != parent) {
          depth[y] = depth[x] + w;
          dfs(y, x);
        }
      }
    }
    
    void solve(int x, int parent) {
      for (auto e : adj[x]) {
        int y = e.first, w = e.second;
        if (y != parent) {
          solve(y, x);
          now = -depth[y];
          info[y].maintain(-(depth[x] + 1));
          now = -(depth[x] + 1);
          info[x].merge(info[y]);
        }
      }
      now = -(depth[x] + 1);
      for (auto &e : fish[x]) {
        e.second = info[x].reset(e.first, e.second);
      }
      info[x].maintain(-depth[x]);
      now = -depth[x];
      for (auto e : fish[x]) {
        info[x].insert(node_t(e.first, -1, e.second, now));
        info[x].insert(node_t(e.first, 1, e.second, now));
      }
    }
    
    int main() {
    #ifdef wxh010910
      freopen("input.txt", "r", stdin);
    #endif
      scanf("%d", &n);
      for (int i = 1; i < n; ++i) {
        int x, y, w;
        scanf("%d %d %d", &x, &y, &w);
        --x;
        --y;
        w <<= 1;
        adj[x].emplace_back(y, w);
        adj[y].emplace_back(x, w);
      }
      scanf("%d", &m);
      for (int i = 1; i <= m; ++i) {
        int day, value, where;
        scanf("%d %d %d", &day, &value, &where);
        day <<= 1;
        --where;
        fish[where].emplace_back(day, value);
      }
      dfs(0, -1);
      solve(0, -1);
      info[0].maintain(INT_MAX);
      printf("%d\n", info[0].query());
      return 0;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值