JOI Spring Camp 2019 简要题解

本文详细介绍了JOI Spring Camp 2019中的算法问题,涉及Day 1的examination和naan,Day 2的antennas和dishes等题目。通过三维数点、贪心策略、线段树和Dijkstra算法等多种方法解决各题,展示了全面的解题思路和技巧。
摘要由CSDN通过智能技术生成

Day 1

examination

直接上三维数点就能过,也可以容斥一下变成二维数点。

#include <bits/stdc++.h>

using namespace std;

template<typename T>
class fenwick {
   
 public:
  vector<T> fenw;
  int n;

  fenwick(int n): n(n) {
   
    fenw.resize(n);
  }

  void modify(int x, T v) {
   
    while (x < n) {
   
      fenw[x] += v;
      x |= x + 1;
    }
  }

  T query(int x) {
   
    T res{
   };
    while (x >= 0) {
   
      res += fenw[x];
      x = (x & (x + 1)) - 1;
    }
    return res;
  }
};

struct event {
   
  int x, y, z, id;

  event(int x, int y, int z, int id): x(x), y(y), z(z), id(id) {
   
  }

  bool operator < (const event &other) {
   
    return make_tuple(x, y, z, id) < make_tuple(other.x, other.y, other.z, other.id);
  }
};

int main() {
   
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  ios::sync_with_stdio(false);
  cin.tie(0);
  cout.tie(0);
  int n, m;
  cin >> n >> m;
  vector<event> events;
  vector<int> disc_x, disc_y, disc_z;
  for (int i = 0; i < n; ++i) {
   
    int x, y, z;
    cin >> x >> y;
    z = x + y;
    x = -x;
    y = -y;
    z = -z;
    disc_x.push_back(x);
    disc_y.push_back(y);
    disc_z.push_back(z);
    events.emplace_back(x, y, z, -1);
  }
  for (int i = 0; i < m; ++i) {
   
    int x, y, z;
    cin >> x >> y >> z;
    x = -x;
    y = -y;
    z = -z;
    disc_x.push_back(x);
    disc_y.push_back(y);
    disc_z.push_back(z);
    events.emplace_back(x, y, z, i);
  }
  sort(disc_x.begin(), disc_x.end());
  disc_x.erase(unique(disc_x.begin(), disc_x.end()), disc_x.end());
  sort(disc_y.begin(), disc_y.end());
  disc_y.erase(unique(disc_y.begin(), disc_y.end()), disc_y.end());
  sort(disc_z.begin(), disc_z.end());
  disc_z.erase(unique(disc_z.begin(), disc_z.end()), disc_z.end());
  for (auto &p : events) {
   
    p.x = lower_bound(disc_x.begin(), disc_x.end(), p.x) - disc_x.begin();
    p.y = lower_bound(disc_y.begin(), disc_y.end(), p.y) - disc_y.begin();
    p.z = lower_bound(disc_z.begin(), disc_z.end(), p.z) - disc_z.begin();
  }
  sort(events.begin(), events.end());
  fenwick<int> fenw(disc_z.size());
  vector<int> ans(m);
  function<void(vector<event>)> solve = [&](vector<event> events) {
   
    if ((int) events.size() == 1) {
   
      return;
    }
    int n = events.size();
    solve(vector<event>(events.begin(), events.begin() + (n / 2)));
    solve(vector<event>(events.begin() + (n / 2), events.end()));
    vector<event> new_events;
    for (int i = 0; i < (n / 2); ++i) {
   
      if (events[i].id == -1) {
   
        new_events.emplace_back(0, events[i].y, events[i].z, events[i].id);
      }
    }
    for (int i = (n / 2); i < n; ++i) {
   
      if (events[i].id != -1) {
   
        new_events.emplace_back(0, events[i].y, events[i].z, events[i].id);
      }
    }
    sort(new_events.begin(), new_events.end());
    for (auto &p : new_events) {
   
      if (p.id == -1) {
   
        fenw.modify(p.z, 1);
      } else {
   
        ans[p.id] += fenw.query(p.z);
      }
    }
    for (auto &p : new_events) {
   
      if (p.id == -1) {
   
        fenw.modify(p.z, -1);
      }
    }
  };
  solve(events);
  for (int i = 0; i < m; ++i) {
   
    cout << ans[i] << "\n";
  }
  return 0;
}

naan

不难证明每次贪心选右端点增加最少的是对的,但这样无法输出分数。考虑将 v i , j v_{i,j} vi,j 乘上 n n n ,然后只能在每段的整数位置切割(即在第 j j j 段如果分给 i i i 那么分母是 1 v i , j \frac{1}{v_{i,j}} vi,j1 ),可以证明这样也满足条件。

#include <bits/stdc++.h>

using namespace std;

struct frac {
   
  int pos, num, den;

  frac(int pos = 0, int num = 0, int den = 1): pos(pos), num(num), den(den) {
   
  }

  bool operator < (const frac &other) const {
   
    if (pos != other.pos) {
   
      return pos < other.pos;
    } else {
   
      return (long long) num * other.den < (long long) den * other.num;
    }
  }
};

int main() {
   
#ifdef wxh010910
  freopen("input.txt", "r", stdin);
#endif
  ios::sync_with_stdio(false);
  cin.tie(0);
  cout.tie(0);
  int n, m;
  cin >> n >> m;
  vector<vector<int>> a(n, vector<int>(m + 1));
  vector<vector<long long>> sum(n, vector<long long>(m + 1));
  vector<int> need(n);
  for (int i = 0; i < n; ++i) {
   
    for (int j = 0; j < m; ++j) {
   
      cin >> a[i][j];
      need[i] += a[i][j];
      a[i][j] *= n;
      sum[i][j + 1] = sum[i][j] + a[i][j];
    }
    a[i][m] = 1;
  }
  vector<bool> used(n);
  vector<int> ptr(n);
  vector<int> p(n);
  frac cur(0, 0, 1);
  auto get_pos = [&](int i, frac l) {
   
    return ((long long) l.num * a[i][l.pos] + l.den - 1) / l.den;
  };
  auto get_sum = [&](int i, frac l, int r) {
   
    return sum[i][r] - sum[i][l.pos] - get_pos(i, l);
  };
  for (int i = 0; i < n; ++i) {
   
    frac min_right(m + 1, 0, 1);
    int min_pos = -1;
    for (int j = 0; j < n; ++j) {
   
      if (!used[j]) {
   
        ptr[j] = max(ptr[j], cur.pos);
        while (ptr[j] < m && get_sum(j, cur, ptr[j] + 1) <= need[j]) {
   
          ++ptr[j];
        }
        int cut = 0;
        if (ptr[j] == cur.pos) {
   
          cut = get_pos(j, cur) + need[j];
        } else {
   
          cut = need[j] - get_sum(j, cur, ptr[j]);
        }
        if (cut == a[j][ptr[j]]) {
   
          ++ptr[j];
          cut = 0;
        }
        if (frac(ptr[j], cut, a[j][ptr[j]]) < min_right) {
   
          min_right = frac(ptr[j], cut, a[j][ptr[j]]);
          min_pos = j;
        }
      }
    }
    used[min_pos] = true;
    cur = min_right;
    p[i] = min_pos;
    if (i != n - 1) {
   
      cout << (long long) min_right.pos * min_right.den + min_right.num << " " << min_right.den << "\n";
    }
  }
  for (int i = 0; i < n; ++i) {
   
    if (i) {
   
      cout << " ";
    }
    cout << p[i] + 1;
  }
  cout << "\n";
  return 0;
}

meetings

原题链接

贴一下代码?(做法就随机点分)

#include <bits/stdc++.h>
#include "meetings.h"

using namespace std;

void add(int x, int y) {
   
  if (x > y) {
   
    swap(x, y);
  }
  Bridge(x, y);
}

void solve(int x, int y, vector<int> all) {
   
  if (all.empty()) {
   
    add(x, y);
    return;
  }
  if ((int) all.size() == 1) {
   
    add(x, *all.begin());
    add(y, *all.begin());
    return;
  }
  int z = all[rand() % all.size()];
  vector<int> l, r;
  for (auto w : all) {
   
    if (w != z) {
   
      if (Query(x, z, w) == w) {
   
        l.push_back(w);
      } else {
   
        r.push_back(w);
      }
    }
  }
  solve(x, z, l);
  solve(y, z, r);
}

void solve(int x, vector<int> all) {
   
  if (all.empty()) {
   
    return;
  }
  if ((int) all.size() == 1) {
   
    add(x, *all.begin());
    return;
  }
  int y = all[rand() % all.size()];
  map<int, vector<int>> f;
  for (auto z : all) {
   
    if (z != y) {
   
      int res = Query(x, y, z);
      if (res == z) {
   
        f[res];
      } else {
   
        f[res].push_back(z);
      }
    }
  }
  vector<int> chain;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值