zoukankan      html  css  js  c++  java
  • 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_1le jle 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
    ", 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
    ", 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 !
    ";
      //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
    ", h[i].size());
      }
      //exit(0);
      //cerr << "min_h" << ' ' << min_h << '
    ';
      //assert(min_h > 0);
      //cerr << "ok2 !
    ";
      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
    ", tot);
      //for (rint i = 1; i <= tot; i++) {
      //  printf("%d
    ", G[i].size());
      //}
      //exit(0);
      //cerr << "ok3 !
    ";
      for (rint i = 1; i <= tot; i++) if (!dfn[i]) {
        tarjan(i);
      }
      //cerr << "ok4 !
    ";
      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 !
    ";
      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
    ", 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! 
    ";
      assert(L[0] <= R[0] && L[1] <= R[1] && L[2] <= R[2] && L[3] <= R[3]);
      printf("%d %d
    ", x2, L[1]);
      printf("%d %d
    ", x1, L[0]);
      printf("%d %d
    ", L[3], y2);
      printf("%d %d
    ", 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 应对方案

  • 相关阅读:
    字母次数
    hdu 2051 Bitset(十进制到二进制)
    练习1升级
    实验一写能自动生成小学四则运算题目的程序
    TCP/IP bad check sum
    Lua GC 之 Ephemeron
    RHEL6下VNC安装和配置
    qpid安装
    关闭中国电信无线客户端自动更新
    Python GC
  • 原文地址:https://www.cnblogs.com/wlzhouzhuan/p/12577508.html
Copyright © 2011-2022 走看看