JOISC2020 题解

Day1T1 建筑装饰4

题目链接:Day1T1 建筑装饰4

Solution

我们先考虑朴素的\(dp\)方法:
\(dp_{i,j,k}\)表示前\(i\)个数中,选了\(j\)\(B\)数组中的数,并且第\(i\)个数选的是\(A/B\)时,是否存在满足单调不降的数列。
复杂度:\(O(n^2)\),期望得分:\(11\)分。
考虑如何优化。可以打个表,发现当\(i,k\)固定的时候,满足\(dp_{i,j,k}=1\)\(j\)刚好是一个区间。
我们可以大胆猜结论:如果\(dp_{i,j_1,k}=1\)并且\(dp_{i,j_2,k}=1\),那么对于任意\(j_1\le j\le j_2\),都满足\(dp_{i,j,k}=1\)
于是,我们可以记录数组\(l_{i,k}\)\(r_{i,k}\)分别表示最小的\(j\)和最大的\(j\)
输出路径的时候从后往前找即可。
复杂度:\(O(n)\),期望得分:\(100\)分。

Code

// Author: wlzhouzhuan
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include <bits/stdc++.h>
using namespace std;

#define ll long long
#define ull unsigned long long
#define rint register int
#define rep(i, l, r) for (rint i = l; i <= r; i++)
#define per(i, l, r) for (rint i = l; i >= r; i--)
#define mset(s, _) memset(s, _, sizeof(s))
#define pb push_back
#define pii pair <int, int>
#define mp(a, b) make_pair(a, b)

inline int read() {
  int x = 0, neg = 1; char op = getchar();
  while (!isdigit(op)) { if (op == '-') neg = -1; op = getchar(); }
  while (isdigit(op)) { x = 10 * x + op - '0'; op = getchar(); }
  return neg * x;
}
inline void print(int x) {
  if (x < 0) { putchar('-'); x = -x; }
  if (x >= 10) print(x / 10);
  putchar(x % 10 + '0');
}

const int inf = 1e9;
const int N = 1000005;
int l[N][2], r[N][2];
int a[N][2], n;

void ckmin(int &a, int b) {
  if (a > b) a = b;
} 
void ckmax(int &a, int b) {
  if (a < b) a = b;
}

int main() {
//  freopen("building.in", "r", stdin);
//  freopen("building.out", "w", stdout);

  n = read();
  a[0][0] = a[0][1] = -inf, a[2 * n + 1][0] = a[2 * n + 1][1] = inf;
  for (rint i = 1; i <= 2 * n; i++) a[i][0] = read();
  for (rint i = 1; i <= 2 * n; i++) a[i][1] = read();
  for (rint i = 1; i <= 2 * n; i++) {
    for (rint j = 0; j <= 1; j++) {
      l[i][j] = inf, r[i][j] = -inf;
      for (rint k = 0; k <= 1; k++) {
        if (a[i][j] >= a[i - 1][k]) {
          ckmin(l[i][j], l[i - 1][k] + (j == 0));
          ckmax(r[i][j], r[i - 1][k] + (j == 0));
        }
      }
    }
  }
  stack <int> ans;
  if (l[2 * n][0] <= n && n <= r[2 * n][0]) {
    int lef = n, st = 0;
    ans.push(0); 
    for (rint i = 2 * n - 1; i >= 1; i--) {
      if (st == 0) {
        lef--;
        for (rint j = 0; j <= 1; j++) {
          if (a[i + 1][st] >= a[i][j] && l[i][j] <= lef && lef <= r[i][j]) {
            st = j;
            break;
          }
        }
      } else {
        for (rint j = 0; j <= 1; j++) {
          if (a[i + 1][st] >= a[i][j] && l[i][j] <= lef && lef <= r[i][j]) {
            st = j;
            break;
          }
        }
      }
      ans.push(st);
    }
    while (!ans.empty()) {
      if (ans.top() == 0) putchar('A');
      else putchar('B');
      ans.pop();
    }
    puts("");
  } else if (l[2 * n][1] <= n && n <= r[2 * n][1]) {
    int lef = n, st = 1;
    ans.push(1);
    for (rint i = 2 * n - 1; i >= 1; i--) {
      if (st == 0) {
        lef--;
        for (rint j = 0; j <= 1; j++) {
          if (a[i + 1][st] >= a[i][j] && l[i][j] <= lef && lef <= r[i][j]) {
            st = j;
            break;
          }
        }
      } else {
        for (rint j = 0; j <= 1; j++) {
          if (a[i + 1][st] >= a[i][j] && l[i][j] <= lef && lef <= r[i][j]) {
            st = j;
            break;
          }
        }
      }
      ans.push(st);
    }
    while (!ans.empty()) {
      if (ans.top() == 0) putchar('A');
      else putchar('B');
      ans.pop();
    }
    puts("");
  } else {
    puts("-1");
  }
  return 0;
}

Day1T2 汉堡肉

题目链接:Day1T2 汉堡肉

Solution

对于\(k=1\),显然这个点位于这\(n\)个矩形的交。
复杂度:\(O(n)\),期望得分:\(2\)分。
对于\(k=2\),令\(r=min_{i=1}^{n}R_i\),令其中一个点的横坐标为\(r\),同时枚举它所有的可能纵坐标,并且包含这个点的矩形删掉。
剩余的矩形按\(k=1\)的做法即可得到另一个点。
复杂度:\(O(n^2)\),期望得分:\(1\)分,结合\(k=1\)可得\(3\)分。
考虑如何优化。我们先将所有的矩形按照\(R_i\)优化,然后在扫描线的过程中记录剩余矩形中的\(max(L_i),max(D_i),min(R_i),min(U_i)\)
具体过程可以用优先队列来实现。
复杂度:\(O(nlogn)\),期望得分:\(3\)分,结合\(k=1\)可得\(6\)分。
对于剩下的点,我采用了随机化做法。
先随机打乱这\(n\)个矩形,然后将前\(k\)个矩形分别放置这\(k\)类。
接下来,将第\(k+1到n\)这些矩形依次插入这\(k\)类。
定义估价函数\(h(rec_x,rec_y)=\frac{area(merge(rec_x,rec_y))}{area(rec_y)}\)表示覆盖率。
我们可以去寻找第\(i\)个矩形与第\(j\)类矩阵交后的估价函数最大值。
那么估价函数越高,即越优秀,匹配成功的概率最大。
按照如上方式随机化,可以通过此题。
复杂度:\(O(欧皇)\),期望得分:\(100\)分。

Code

// Author: wlzhouzhuan
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include <bits/stdc++.h>
using namespace std;

#define ll long long
#define ull unsigned long long
#define rint register int
#define rep(i, l, r) for (rint i = l; i <= r; i++)
#define per(i, l, r) for (rint i = l; i >= r; i--)
#define mset(s, _) memset(s, _, sizeof(s))
#define pb push_back
#define pii pair <int, int>
#define mp(a, b) make_pair(a, b)

inline int read() {
  int x = 0, neg = 1; char op = getchar();
  while (!isdigit(op)) { if (op == '-') neg = -1; op = getchar(); }
  while (isdigit(op)) { x = 10 * x + op - '0'; op = getchar(); }
  return neg * x;
}
inline void print(int x) {
  if (x < 0) { putchar('-'); x = -x; }
  if (x >= 10) print(x / 10);
  putchar(x % 10 + '0');
}


const int N = 200005;
int n, k;
struct rect {
  ll x1, y1, x2, y2;
  rect(ll _x1 = 0, ll _y1 = 0, ll _x2 = 0, ll _y2 = 0) {
    x1 = _x1, y1 = _y1, x2 = _x2, y2 = _y2;
  }
} a[N], b[5];

rect merge(rect x, rect y) {
  return rect(max(x.x1, y.x1), max(x.y1, y.y1), min(x.x2, y.x2), min(x.y2, y.y2));
}
ll area(rect x) { // 矩形内包含多少点 
  if (x.x1 > x.x2 || x.y1 > x.y2) return 0;
  else return (x.x2 - x.x1 + 1) * (x.y2 - x.y1 + 1);
}

double h(rect x, rect y) {
  rect m = merge(x, y);
  return area(m) / 1.0 / area(y);
}

int main() {
  srand(time(NULL));
  scanf("%d%d", &n, &k);
  for (rint i = 1; i <= n; i++) {
    scanf("%lld%lld%lld%lld", &a[i].x1, &a[i].y1, &a[i].x2, &a[i].y2);
  }
  while (true) {
    int ok = 1;
    random_shuffle(a + 1, a + n + 1);
    for (rint i = 1; i <= k; i++) b[i] = a[i];
    for (rint i = k + 1; i <= n; i++) {
      double max_h = -1e9; int id = 1;
      for (rint j = 1; j <= k; j++) {
        double _h = h(a[i], b[j]);
        if (_h > max_h) {
          max_h = _h;
          id = j;
        }
      }
      b[id] = merge(b[id], a[i]);
      if (area(b[id]) <= 0) {
        ok = 0;
        break;
      }
    }
    if (ok) break;
  }
  for (rint i = 1; i <= k; i++) {
    printf("%lld %lld\n", b[i].x1, b[i].y1);
  }
  return 0;
}

谈谈正经的做法

// Author: wlzhouzhuan
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#include <bits/stdc++.h>
using namespace std;

#define ll long long
#define ull unsigned long long
#define rint register int
#define rep(i, l, r) for (rint i = l; i <= r; i++)
#define per(i, l, r) for (rint i = l; i >= r; i--)
#define mset(s, _) memset(s, _, sizeof(s))
#define pb push_back
#define pii pair <int, int>
#define mp(a, b) make_pair(a, b)
#define all(x) x.begin(), x.end()

inline int read() {
  int x = 0, neg = 1; char op = getchar();
  while (!isdigit(op)) { if (op == '-') neg = -1; op = getchar(); }
  while (isdigit(op)) { x = 10 * x + op - '0'; op = getchar(); }
  return neg * x;
}
inline void print(int x) {
  if (x < 0) { putchar('-'); x = -x; }
  if (x >= 10) print(x / 10);
  putchar(x % 10 + '0');
}

inline void ckmin(int &a, int b) {
  if (a > b) a = b;
}
inline void ckmax(int &a, int b) {
  if (a < b) a = b;
}

const int N = 1500005;
const int inf = 1e9;

struct rect {
  int x1, y1, x2, y2;
} a[N];
int n, K;

pair <int, int> ans[5];
int cnt[N];
void modify(int x, int y, int v) {
  for (rint i = 1; i <= n; i++) {
    if (a[i].x1 <= x && x <= a[i].x2 && a[i].y1 <= y && y <= a[i].y2) {
      cnt[i] += v; 
    }
  }
}
void dfs(int d) {
  int x1 = -inf, x2 = inf, y1 = -inf, y2 = inf;
  for (rint i = 1; i <= n; i++) if (!cnt[i]) {
    ckmax(x1, a[i].x1), ckmax(y1, a[i].y1);
    ckmin(x2, a[i].x2), ckmin(y2, a[i].y2);
  }
  if (d == 1) {
    if (x1 <= x2 && y1 <= y2) { // 最后一个点在剩余矩形的交内 
      ans[1] = make_pair(x1, y1);
      for (rint i = 1; i <= K; i++) {
        printf("%d %d\n", ans[i].first, ans[i].second);
      }
      exit(0);
    }
    return ;
  }
  for (rint cx = 0; cx < 2; cx++) {
    for (rint cy = 0; cy < 2; cy++) {
      int px = (cx ? x1 : x2);
      int py = (cy ? y1 : y2);
      modify(px, py, 1);
      ans[d] = make_pair(px, py);
      dfs(d - 1);
      modify(px, py, -1);
    }
  }
}

vector <int> h[N];
vector <tuple <int, int, int>> sl[4], sr[4];
int pre[4][N], suf[4][N], id[N][4];

int fir(tuple <int, int, int> a) {
  return get<0>(a);
}
int sec(tuple <int, int, int> b) {
  return get<1>(b);
}
int thi(tuple <int, int, int> c) {
  return get<2>(c);
}

vector <int> G[N];
void add(int u, int v) { G[u].push_back(v); }
int dfn[N], low[N], dtot;
int ins[N], be[N], scc;
stack <int> st;
void tarjan(int u) {
  dfn[u] = low[u] = ++dtot;
  st.push(u), ins[u] = 1;
  for (auto v: G[u]) {
    if (!dfn[v]) {
      tarjan(v);
      low[u] = min(low[u], low[v]);
    } else if (ins[v]) {
      low[u] = min(low[u], dfn[v]);
    }
  }
  if (dfn[u] == low[u]) {
    int y; ++scc;
    while (y = st.top()) {
      ins[y] = 0, st.pop();
      be[y] = scc;
      if (y == u) break;
    }
  }
}
void solve() {
  int x1 = -inf, x2 = inf, y1 = -inf, y2 = inf;
  for (rint i = 1; i <= n; i++) {
    ckmax(x1, a[i].x1), ckmax(y1, a[i].y1);
    ckmin(x2, a[i].x2), ckmin(y2, a[i].y2);
  }
  //cerr << "ok1 !\n";
  //assert(x1 >= x2 && y1 >= y2);
  int tot = 0, min_h = inf;
  for (rint i = 1; i <= n; i++) {
    vector <int> o = h[i];
    // 两条纵边  (0 <= id <= 1)
    if (a[i].x1 <= x1 && x1 <= a[i].x2) o.pb(0);
    if (a[i].x1 <= x2 && x2 <= a[i].x2) o.pb(1);
    // 两条横边 (2 <= id <= 3)
    if (a[i].y1 <= y1 && y1 <= a[i].y2) o.pb(2);
    if (a[i].y1 <= y2 && y2 <= a[i].y2) o.pb(3);
    ckmin(min_h, o.size());
    if (o.size() >= 3) { // 一定与一条边有交
      //o.clear();
      continue;
    }
    id[i][0] = ++tot, id[i][1] = ++tot;
    if (o.size() == 1) add(id[i][1], id[i][0]);
    int siz = o.size();
    for (rint j = 0; j < siz; j++) {
      sl[o[j]].emplace_back(o[j] < 2 ? max(y2, a[i].y1) : max(x2, a[i].x1), id[i][j], id[i][j ^ 1]);
      sr[o[j]].emplace_back(o[j] < 2 ? min(y1, a[i].y2) : min(x1, a[i].x2), id[i][j], id[i][j ^ 1]);
    }
    //o.pb(0);
    h[i] = o;
    //printf("%d\n", h[i].size());
  }
  //exit(0);
  //cerr << "min_h" << ' ' << min_h << '\n';
  //assert(min_h > 0);
  //cerr << "ok2 !\n";
  for (rint v = 0; v < 4; v++) {
    sort(all(sl[v])), sort(all(sr[v]));
    int sz_l = sl[v].size(), sz_r = sr[v].size();
    for (rint i = 0; i < sz_r; i++) {
      pre[v][i + 1] = ++tot;
      if (i > 0) add(pre[v][i + 1], pre[v][i]); 
      add(pre[v][i + 1], thi(sr[v][i]));
    }
    for (rint i = 0, j = 0; i < sz_l; i++) {
      while (j < sz_r && fir(sr[v][j]) < fir(sl[v][i])) j++;
      if (j > 0) add(sec(sl[v][i]), pre[v][j]);
    }
    for (rint i = sz_l - 1; i >= 0; i--) {
      suf[v][i] = ++tot;
      if (i + 1 < sz_l) add(suf[v][i], suf[v][i + 1]);
      add(suf[v][i], thi(sl[v][i]));
    }
    for (rint i = sz_r - 1, j = sz_l; i >= 0; i--) {
      while (j > 0 && fir(sl[v][j - 1]) > fir(sr[v][i])) j--;
      if (j < sz_l) add(sec(sr[v][i]), suf[v][j]);
    }
  }
  //printf("tot=%d\n", tot);
  //for (rint i = 1; i <= tot; i++) {
  //  printf("%d\n", G[i].size());
  //}
  //exit(0);
  //cerr << "ok3 !\n";
  for (rint i = 1; i <= tot; i++) if (!dfn[i]) {
    tarjan(i);
  }
  //cerr << "ok4 !\n";
  vector <int> L(5), R(4);
  L[0] = L[1] = y2, L[2] = L[3] = x2;
  R[0] = R[1] = y1, R[2] = R[3] = x1;
  //cerr << "ok5 !\n";
  for (rint i = 1; i <= n; i++) {
    if (!id[i][0]) continue;
    assert(h[i].size());
    int j = h[i][be[id[i][0]] >= be[id[i][1]]]; // 拓扑序反过来 
    //printf("%d: %d\n", i, be[id[i][0]] >= be[id[i][1]]);
    ckmax(L[j], j < 2 ? a[i].y1 : a[i].x1);
    ckmin(R[j], j < 2 ? a[i].y2 : a[i].x2);
  }
  //cerr << "ok6! \n";
  assert(L[0] <= R[0] && L[1] <= R[1] && L[2] <= R[2] && L[3] <= R[3]);
  printf("%d %d\n", x2, L[1]);
  printf("%d %d\n", x1, L[0]);
  printf("%d %d\n", L[3], y2);
  printf("%d %d\n", L[2], y1);
}

int main() {
  //freopen("04-14.txt.in", "r", stdin);
  //freopen("my.out", "w", stdout);
  scanf("%d%d", &n, &K);
  for (rint i = 1; i <= n; i++) {
    scanf("%d%d%d%d", &a[i].x1, &a[i].y1, &a[i].x2, &a[i].y2);
  }
  dfs(K);
  assert(K == 4);
  solve();
  return 0;
}

Day1T3 扫除

题目链接:Day1T3 扫除

Solution

Code

咕咕咕

Day2T1 变色龙之恋

题目链接:Day2T1 变色龙之恋

Day2T2 有趣的Joitter交友

题目链接:Day2T2 有趣的Joitter交友

Day2T3 遗迹

题目链接:Day2T3 遗迹

Day3T1 星座3

题目链接:Day3T1 星座3

Day3T2 收获

题目链接:Day3T2 收获

Day3T3 迷路的猫

题目链接:Day3T3 迷路的猫

Day4T1 首都

题目链接:Day4T1 首都

Day4T2 制作团子

题目链接:Day4T2 制作团子

Day4T3 应对方案

题目链接:Day4T3 应对方案

posted @ 2020-03-26 21:30  wlzhouzhuan  阅读(510)  评论(0编辑  收藏  举报