Submission #217171

#TimeUsernameProblemLanguageResultExecution timeMemory
217171rama_pangTreatment Project (JOI20_treatment)C++14
100 / 100
256 ms15420 KiB
#include <bits/stdc++.h>
using namespace std;

class TreatmentProject {
 
 private:
  
  template<typename T> using min_queue = priority_queue<T, vector<T>, greater<T>>;

  struct Project {
    int T, L, R, C;
    Project() {}
    Project(int T, int L, int R, int C) : T(T), L(L), R(R), C(C) {}
  };

  int N_, M_;
  vector<Project> project_;

  long long NaiveDijkstraDP() { // O(M^2) Dijsktra-DP solution (naive)
    int N = N_, M = M_;
    vector<Project> project = project_;
    
    assert(M <= 5000);

    for (int i = 0; i < M; i++) {
      project[i].R++;
    } // project[i] cures [L[i], R[i])


    vector<long long> dp(M, 1e18);
    vector<bool> vis(M, false);

    int current = -1;

    for (int i = 0; i < M; i++) {
      if (project[i].R > N) {
        dp[i] = project[i].C;
        if (current == -1 || dp[current] > dp[i]) {
          current = i;
        }
      }
    }

    while (current != -1) { // Dijkstra's Algorithm
      vis[current] = true;

      for (int now = 0; now < M; now++) {
        if (vis[now]) continue;

        bool CanTransition = false;
        if (project[now].T >= project[current].T) {
          CanTransition = project[now].R >= project[current].L + (project[now].T - project[current].T);
        } else {
          CanTransition = project[now].R - (project[current].T - project[now].T) >= project[current].L;
        }

        if (CanTransition) {
          dp[now] = min(dp[now], project[now].C + dp[current]);
        }
      }

      int nxt = -1;

      for (int now = 0; now < M; now++) {
        if (vis[now]) continue;
        if (nxt == -1 || dp[nxt] > dp[now]) {
          nxt = now;
        }
      }

      current = nxt;
    }

    long long res = 1e18;

    for (int i = 0; i < M; i++) {
      if (project[i].L == 1) {
        res = min(res, dp[i]);
      }
    }

    if (res == (long long) 1e18) { // no solution exists
      res = -1;
    }

    return res;
  }

  class RangeTree { // O(log^2 M) per operation
   
   private:

    class DisjointSet {
     
     private:

      int BASE = 5;
      vector<int> p;

      int Find_(int x) {
        return p[x] == x ? x : p[x] = Find_(p[x]);
      }

      bool Union_(int x, int y) { // p[x] = y
        x = Find_(x), y = Find_(y);
        if (x != y) {
          p[x] = y;
          return true;
        } else {
          return false;
        }
      }

     public:

      DisjointSet() {}

      void Init(int n) {
        p.resize(n + BASE + BASE);
        iota(begin(p), end(p), 0);
      }

      int Find(int x) {
        return Find_(x + BASE) - BASE;
      }

      bool Union(int x, int y) {
        return Union_(x + BASE, y + BASE);
      }

    };

    int sz;

    vector<vector<pair<int, int>>> tree; // sorted vector (time, index)
    vector<DisjointSet> dsu;

    int LowerBound(int n, pair<int, int> x) { // lower_bound on tree[n]
      int lo = 0, hi = tree[n].size();

      while (lo < hi) {
        int mid = (lo + hi) / 2;
        if (mid >= 0 && tree[n][mid] >= x) {
          hi = mid;
        } else {
          lo = mid + 1;
        }
      }

      return dsu[n].Find(hi);
    }

    bool VectorErase(int n, pair<int, int> x) {
      int pos = LowerBound(n, x);
      if (pos < tree[n].size() && tree[n][pos] == x) {
        dsu[n].Union(pos, pos + 1);
        return true;
      } else {
        return false;
      }
    }

    vector<Project> project;
    vector<pair<int, int>> base;

    void Pull(int n) {
      dsu[n].Init(tree[n * 2].size() + tree[n * 2 + 1].size());
      tree[n].resize(tree[n * 2].size() + tree[n * 2 + 1].size());
      merge(begin(tree[n * 2]), end(tree[n * 2]), begin(tree[n * 2 + 1]), end(tree[n * 2 + 1]), begin(tree[n]));
    }

    void Build(int n, int tl, int tr, const vector<pair<int, int>> &a) { // tree = (time, index)
      if (tl == tr) {
        dsu[n].Init(1);
        tree[n].emplace_back(make_pair(project[a[tl].second].T, a[tl].second));
        return;
      }
      int mid = (tl + tr) / 2;
      Build(n * 2, tl, mid, a);
      Build(n * 2 + 1, mid + 1, tr, a);
      Pull(n);
    }

    void Erase(int n, int tl, int tr, const pair<int, int> &a) { // element a to be deleted from segment tree
      if (!VectorErase(n, a)) return;
      if (tl == tr) return;
      int mid = (tl + tr) / 2;
      Erase(n * 2, tl, mid, a);
      Erase(n * 2 + 1, mid + 1, tr, a);
    }

    void Query(int n, int tl, int tr, int l, int r, int t1, int t2, vector<int> &res) {
      if (tr < l || r < tl || tl > tr || l > r) return;
      if (l <= tl && tr <= r) {
        int pos = LowerBound(n, make_pair(t1, INT_MIN));
        while (pos < tree[n].size()) {
          if (tree[n][pos].first > t2) break;
          assert(t1 <= tree[n][pos].first && tree[n][pos].first <= t2);
          res.emplace_back(tree[n][pos].second);
          pos = dsu[n].Find(pos + 1);
        }
        return;
      }
      int mid = (tl + tr) / 2;
      Query(n * 2, tl, mid, l, r, t1, t2, res);
      Query(n * 2 + 1, mid + 1, tr, l, r, t1, t2, res);
    }

   public:

    RangeTree(int n, const vector<Project> &proj) {
      sz = n;
      project = proj;
      tree.resize(4 * n);
      dsu.resize(4 * n);
    }

    void Build(const vector<pair<int, int>> &a) {
      base = a;
      return Build(1, 0, sz - 1, a);
    }

    void Erase(int x) {
      return Erase(1, 0, sz - 1, pair<int, int>(project[x].T, x));
    }

    void Query(int l, int r, int t1, int t2, vector<int> &res) { // get ids from segment [l, r] with time <= t, and place it into res
      l = lower_bound(begin(base), end(base), make_pair(l, INT_MIN)) - begin(base);
      r = upper_bound(begin(base), end(base), make_pair(r, INT_MAX)) - begin(base) - 1;
      
      return Query(1, 0, sz - 1, l, r, t1, t2, res);
    }

  };

  long long RangeTreeDijkstraDP() { // O(M log^2 M) Dijkstra-DP solution (optimized with RangeTree)
    int N = N_, M = M_;
    vector<Project> project = project_;
 
    for (int i = 0; i < M; i++) {
      project[i].R++;
    }
 
    RangeTree RMinusT(M, project), RPlusT(M, project);
 
    { // Initialize RMinusT (base sorted by R[i] - T[i])
      vector<pair<Project, int>> a;
      for (int i = 0; i < M; i++) {
        a.emplace_back(project[i], i);
      }
 
      sort(begin(a), end(a), [&](const pair<Project, int> &a, const pair<Project, int> &b) {
        return a.first.R - a.first.T < b.first.R - b.first.T;
      });
 
      vector<pair<int, int>> base; // (R - T, index)
      for (auto &i : a) {
        base.emplace_back(i.first.R - i.first.T, i.second);
      }
 
      RMinusT.Build(base);
    }
 
    { // Initialize RPlusT (base sorted by R[i] + T[i])
      vector<pair<Project, int>> a;
      for (int i = 0; i < M; i++) {
        a.emplace_back(project[i], i);
      }
 
      sort(begin(a), end(a), [&](const pair<Project, int> &a, const pair<Project, int> &b) {
        return a.first.R + a.first.T < b.first.R + b.first.T;
      });
 
      vector<pair<int, int>> base; // (R + T, index)
      for (auto &i : a) {
        base.emplace_back(i.first.R + i.first.T, i.second);
      }
 
      RPlusT.Build(base);
    }
 
    min_queue<pair<long long, int>> pq;
    vector<long long> dp(M, 1e18);
    for (int i = 0; i < M; i++) {
      if (project[i].R > N) {
        dp[i] = project[i].C;
        pq.emplace(dp[i], i);
        RMinusT.Erase(i);
        RPlusT.Erase(i);
      }
    }
 
    while (!pq.empty()) {
      int cur = pq.top().second;
      pq.pop();
 
      vector<int> candidates;
      RMinusT.Query(project[cur].L - project[cur].T, INT_MAX, project[cur].T, INT_MAX, candidates);
      RPlusT.Query(project[cur].L + project[cur].T, INT_MAX, 1, project[cur].T, candidates);
      
      sort(begin(candidates), end(candidates));
      candidates.resize(unique(begin(candidates), end(candidates)) - begin(candidates));
 
      for (auto &i : candidates) {
        dp[i] = project[i].C + dp[cur];
        pq.emplace(dp[i], i);
 
        RMinusT.Erase(i);
        RPlusT.Erase(i);
      }
    }
 
    long long res = 1e18;
 
    for (int i = 0; i < M; i++) {
      if (project[i].L == 1) {
        res = min(res, dp[i]);
      }
    }
 
    if (res == (long long) 1e18) {
      res = -1;
    }
 
    return res;
  }
 
  class SegmentTree { // O(log M) per operation
   private:

    int sz;

    vector<pair<int, int>> tree_max; // (time, index), sorted by R - T
    vector<pair<int, int>> tree_min; // (time, index), sorted by R + T
    
    vector<Project> project;
    vector<pair<int, int>> base_max;
    vector<pair<int, int>> base_min;
    vector<int> reverse_max;
    vector<int> reverse_min;

   public:

    SegmentTree(int n, const vector<Project> &p) {
      sz = n;
      project = p;
      tree_max.assign(2 * sz, {-1, -1});
      tree_min.assign(2 * sz, {INT_MAX, -1});
    }

    void BuildMax(const vector<pair<int, int>> &a) {
      base_max = a;
      reverse_max.resize(a.size());
      for (int i = 0; i < a.size(); i++) {
        tree_max[i + sz] = make_pair(project[a[i].second].T, a[i].second);
        reverse_max[a[i].second] = i;
      }
      for (int i = sz - 1; i > 0; i--) {
        tree_max[i] = max(tree_max[i * 2], tree_max[i * 2 + 1]);
      }
    }

    void BuildMin(const vector<pair<int, int>> &a) {
      base_min = a;
      reverse_min.resize(a.size());
      for (int i = 0; i < a.size(); i++) {
        tree_min[i + sz] = make_pair(project[a[i].second].T, a[i].second);
        reverse_min[a[i].second] = i;
      }
      for (int i = sz - 1; i > 0; i--) {
        tree_min[i] = min(tree_min[i * 2], tree_min[i * 2 + 1]);
      }
    }

    int QueryMax(int l, int r, int t) { // suffix t...inf
      l = lower_bound(begin(base_max), end(base_max), make_pair(l, INT_MIN)) - begin(base_max);
      r = upper_bound(begin(base_max), end(base_max), make_pair(r, INT_MAX)) - begin(base_max) - 1;
      
      pair<int, int> res = {-1, -1};
      for (l += sz, r += sz + 1; l < r; l /= 2, r /= 2) {
        if (l & 1) res = max(res, tree_max[l++]);
        if (r & 1) res = max(res, tree_max[--r]);
      }

      return res.first < t ? -1 : res.second;
    }

    int QueryMin(int l, int r, int t) { // prefix 1...t
      l = lower_bound(begin(base_min), end(base_min), make_pair(l, INT_MIN)) - begin(base_min);
      r = upper_bound(begin(base_min), end(base_min), make_pair(r, INT_MAX)) - begin(base_min) - 1;
      
      pair<int, int> res = {INT_MAX, -1};
      for (l += sz, r += sz + 1; l < r; l /= 2, r /= 2) {
        if (l & 1) res = min(res, tree_min[l++]);
        if (r & 1) res = min(res, tree_min[--r]);
      }
      return res.first > t ? -1 : res.second;
    }
    
    void EraseMax(int id) {
      id = reverse_max[id];
      tree_max[id += sz] = {-1, -1};
      for (id /= 2; id > 0; id /= 2) {
        tree_max[id] = max(tree_max[id * 2], tree_max[id * 2 + 1]);
      }
    }

    void EraseMin(int id) {
      id = reverse_min[id];
      tree_min[id += sz] = {INT_MAX, -1};
      for (id /= 2; id > 0; id /= 2) {
        tree_min[id] = min(tree_min[id * 2], tree_min[id * 2 + 1]);
      }
    }

  };

  long long SegmentTreeDijkstraDP() { // O(M log M) Dijkstra-DP solution (optimized with SegmentTree)
    int N = N_, M = M_;
    vector<Project> project = project_;

    for (int i = 0; i < M; i++) {
      project[i].R++;
    }

    SegmentTree SegTree(M, project);
    // RangeTree RMinusT(M, project), RPlusT(M, project); // O(log^2 M) per operation

    { // Initialize RMinusT (base sorted by R[i] - T[i])
      vector<pair<Project, int>> a;
      for (int i = 0; i < M; i++) {
        a.emplace_back(project[i], i);
      }

      sort(begin(a), end(a), [&](const pair<Project, int> &a, const pair<Project, int> &b) {
        return a.first.R - a.first.T < b.first.R - b.first.T;
      });

      vector<pair<int, int>> base; // (R - T, index)
      for (auto &i : a) {
        base.emplace_back(i.first.R - i.first.T, i.second);
      }

      SegTree.BuildMax(base);
    }

    { // Initialize RPlusT (base sorted by R[i] + T[i])
      vector<pair<Project, int>> a;
      for (int i = 0; i < M; i++) {
        a.emplace_back(project[i], i);
      }

      sort(begin(a), end(a), [&](const pair<Project, int> &a, const pair<Project, int> &b) {
        return a.first.R + a.first.T < b.first.R + b.first.T;
      });

      vector<pair<int, int>> base; // (R + T, index)
      for (auto &i : a) {
        base.emplace_back(i.first.R + i.first.T, i.second);
      }

      SegTree.BuildMin(base);
    }

    min_queue<pair<long long, int>> pq;
    vector<long long> dp(M, 1e18);
    for (int i = 0; i < M; i++) {
      if (project[i].R > N) {
        dp[i] = project[i].C;
        pq.emplace(dp[i], i);
        SegTree.EraseMax(i);
        SegTree.EraseMin(i);
      }
    }

    while (!pq.empty()) {
      int cur = pq.top().second;
      pq.pop();

      vector<int> candidates;

      while (true) { // get ids which satisfies R - T constraints
        int now = SegTree.QueryMax(project[cur].L - project[cur].T, INT_MAX, project[cur].T);
        if (now == -1) break;

        SegTree.EraseMax(now);
        SegTree.EraseMin(now);
        candidates.emplace_back(now);
      }

      while (true) { // get ids which satisfies R + T constraints
        int now = SegTree.QueryMin(project[cur].L + project[cur].T, INT_MAX, project[cur].T);
        if (now == -1) break;

        SegTree.EraseMax(now);
        SegTree.EraseMin(now);
        candidates.emplace_back(now);
      }

      for (auto &i : candidates) {
        dp[i] = project[i].C + dp[cur];
        pq.emplace(dp[i], i);
      }
    }

    long long res = 1e18;

    for (int i = 0; i < M; i++) {
      if (project[i].L == 1) {
        res = min(res, dp[i]);
      }
    }

    if (res == (long long) 1e18) {
      res = -1;
    }

    return res;
  }

 public:

  void Read() {
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);

    cin >> N_ >> M_;
    for (int i = 0; i < M_; i++) {
      int T, L, R, C;
      cin >> T >> L >> R >> C;
      project_.emplace_back(T, L, R, C);
    }
  }

  long long Solve() {
    return SegmentTreeDijkstraDP();
  }

};

int main() {
  TreatmentProject Solver;
  Solver.Read();
  cout << Solver.Solve() << "\n";
  return 0;
}

Compilation message (stderr)

treatment.cpp: In member function 'bool TreatmentProject::RangeTree::VectorErase(int, std::pair<int, int>)':
treatment.cpp:155:15: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
       if (pos < tree[n].size() && tree[n][pos] == x) {
           ~~~~^~~~~~~~~~~~~~~~
treatment.cpp: In member function 'void TreatmentProject::RangeTree::Query(int, int, int, int, int, int, int, std::vector<int>&)':
treatment.cpp:196:20: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
         while (pos < tree[n].size()) {
                ~~~~^~~~~~~~~~~~~~~~
treatment.cpp: In member function 'void TreatmentProject::SegmentTree::BuildMax(const std::vector<std::pair<int, int> >&)':
treatment.cpp:354:25: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
       for (int i = 0; i < a.size(); i++) {
                       ~~^~~~~~~~~~
treatment.cpp: In member function 'void TreatmentProject::SegmentTree::BuildMin(const std::vector<std::pair<int, int> >&)':
treatment.cpp:366:25: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
       for (int i = 0; i < a.size(); i++) {
                       ~~^~~~~~~~~~
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...
#Verdict Execution timeMemoryGrader output
Fetching results...