CodeForces Gym 101978 简要题解

Contest Environment

如果第二行有障碍,那么一定无解。

否则考虑将 A A A 移过去,对于连续的一段障碍,他需要直接跨越,然后其他人可以任意分配,像这样:

*#*A#.B
***...*

不难发现条件是 . 个数大于等于连续的一段 # 的个数加 3 3 3

#include <bits/stdc++.h>

using namespace std;

int main() {
  int tt;
  cin >> tt;
  for (int ttt = 1; ttt <= tt; ++ttt) {
    string foo, bar;
    cin >> foo >> bar;
    int n = foo.size();
    bool flag = true;
    for (int i = 0; i < n; ++i) {
      if (bar[i] == '#') {
        flag = false;
        break;
      }
    }
    if (!flag) {
      cout << "Case #" << ttt << ": Impossible" << endl;
      continue;
    }
    int free = 0;
    for (int i = 0; i < n; ++i) {
      if (foo[i] == '.') {
        ++free;
      }
      if (bar[i] == '.') {
        ++free;
      }
    }
    int sum = 0, need = 0;
    for (int i = 0; i < n; ++i) {
      if (foo[i] == '#') {
        ++sum;
      } else {
        need = max(need, sum);
        sum = 0;
      }
    }
    cout << "Case #" << ttt << ": " << (free >= need + 3 ? "Possible" : "Impossible") << endl;
  }
  return 0;
}

Stockholm

不难发现一个区域的边界是,先走左儿子,然后一直走右儿子,或者先走右儿子,然后一直走左儿子。两个点的最短路一定会在lca处旁边的一个区域汇合。

#include <bits/stdc++.h>

using namespace std;

int main() {
  int tt;
  cin >> tt;
  for (int ttt = 1; ttt <= tt; ++ttt) {
    vector<map<long long, int>> dist(2);
    for (int index = 0; index < 2; ++index) {
      long long start;
      cin >> start;
      queue<long long> q;
      auto insert = [&](long long x, int y) {
        if (dist[index].find(x) == dist[index].end()) {
          dist[index][x] = y;
          q.push(x);
        }
      };
      auto getl = [&](long long x) {
        while (x > 0 && x % 2 == 1) {
          x = x - 1 >> 1;
        }
        if (x <= 0) {
          return -1ll;
        } else {
          return x - 2 >> 1;
        }
      };
      auto getr = [&](long long x) {
        while (x > 0 && x % 2 == 0) {
          x = x - 2 >> 1;
        }
        if (x <= 0) {
          return -2ll;
        } else {
          return x - 1 >> 1;
        }
      };
      insert(start, 0);
      insert(getl(start), 0);
      insert(getr(start), 0);
      while (!q.empty()) {
        long long x = q.front();
        q.pop();
        insert(getl(x), dist[index][x] + 1);
        insert(getr(x), dist[index][x] + 1);
      }
    }
    int answer = INT_MAX;
    for (auto p : dist[0]) {
      if (dist[1].find(p.first) != dist[1].end()) {
        answer = min(answer, p.second + dist[1][p.first]);
      }
    }
    cout << "Case #" << ttt << ": " << answer << endl;
  }
  return 0;
}

Ethan Sums Shortest Distances

区间DP,如果有一条横着的割边,就划分成两个区间。否则这段区间只有一条竖边,枚举这条竖边计算即可。

#include <bits/stdc++.h>

using namespace std;

int main() {
  int tt;
  cin >> tt;
  for (int ttt = 1; ttt <= tt; ++ttt) {
    int n;
    cin >> n;
    vector<vector<int>> board(2, vector<int> (n));
    for (int i = 0; i < 2; ++i) {
      for (int j = 0; j < n; ++j) {
        cin >> board[i][j];
      }
    }
    vector<int> sum(n + 1);
    for (int i = 0; i < n; ++i) {
      sum[i + 1] = sum[i] + board[0][i] + board[1][i];
    }
    vector<vector<vector<vector<long long>>>> dp(n, vector<vector<vector<long long>>> (n, vector<vector<long long>> (2, vector<long long> (2, 1ll << 60))));
    for (int l = n - 1; ~l; --l) {
      for (int r = l; r < n; ++r) {
        for (int a = 0; a < 2; ++a) {
          for (int b = 0; b < 2; ++b) {
            for (int m = l; m < r; ++m) {
              for (int c = 0; c < 2; ++c) {
                dp[l][r][a][b] = min(dp[l][r][a][b], dp[l][m][a][c] + dp[m + 1][r][c][b] + (long long)sum[m + 1] * (sum[n] - sum[m + 1]));
              }
            }
            for (int m = l; m <= r; ++m) {
              long long up = 0, down = 0;
              for (int i = l; i <= r; ++i) {
                up += board[0][i];
                down += board[1][i];
              }
              long long ul = 0, ur = up;
              long long dl = 0, dr = down;
              if (!a) {
                up += sum[l];
                ul += sum[l];
              } else {
                down += sum[l];
                dl += sum[l];
              }
              if (!b) {
                up += sum[n] - sum[r + 1];
                ur += sum[n] - sum[r + 1];
              } else {
                down += sum[n] - sum[r + 1];
                dr += sum[n] - sum[r + 1];
              }
              long long value = up * down;
              for (int i = l; i < r; ++i) {
                ul += board[0][i];
                ur -= board[0][i];
                dl += board[1][i];
                dr -= board[1][i];
                value += ul * ur + dl * dr;
                if (i < m) {
                  value += ul * down + dl * up;
                } else {
                  value += ur * down + dr * up;
                }
              }
              dp[l][r][a][b] = min(dp[l][r][a][b], value);
            }
          }
        }
      }
    }
    cout << "Case #" << ttt << ": " << dp[0][n - 1][0][0] << endl;
  }
  return 0;
}

Personal Space

一条鱼的活动范围可以写成下面的形式: ( l l , l , r , r r ) (ll, l, r, rr) (ll,l,r,rr) ,表示初始的活动范围是 ( l , r ) (l,r) (l,r) ,移除一个间隔之后活动范围是 ( l l , r ) (ll,r) (ll,r) 或者 ( l , r r ) (l,rr) (l,rr) 。不难发现这样的活动范围只有 O ( n ) O(n) O(n) 个,拿个set提取出来。考虑一个状态能转移到另一个状态当且仅当 r ≤ l l ′ , r r ≤ l ′ r\le ll&#x27;, rr\le l&#x27; rll,rrl ,那么做个DP,对于每个 r r rr rr 维护最多放多少个和放最多鱼的前提下最小的 r r r 即可。

#include <bits/stdc++.h>

using namespace std;

int main() {
  int tt;
  scanf("%d", &tt);
  for (int ttt = 1; ttt <= tt; ++ttt) {
    int n;
    scanf("%d", &n);
    vector<int> x(n), a(n), b(n);
    vector<int> disc;
    for (int i = 0; i < n; ++i) {
      scanf("%d %d %d", &x[i], &a[i], &b[i]);
      ++b[i];
      disc.push_back(x[i]);
    }
    disc.push_back(-1);
    sort(disc.begin(), disc.end());
    disc.erase(unique(disc.begin(), disc.end()), disc.end());
    for (int i = 0; i < n; ++i) {
      x[i] = lower_bound(disc.begin(), disc.end(), x[i]) - disc.begin();
    }
    set<int> s;
    s.insert(-2);
    s.insert(-1);
    s.insert(0);
    s.insert(disc.size());
    s.insert(disc.size() + 1);
    s.insert(disc.size() + 2);
    vector<pair<int, pair<int, int>>> events;
    for (int i = 0; i < n; ++i) {
      events.emplace_back(a[i], make_pair(1, x[i]));
      events.emplace_back(b[i], make_pair(0, x[i]));
    }
    sort(events.begin(), events.end());
    vector<vector<pair<int, pair<int, int>>>> adj(disc.size() + 3);
    vector<pair<int, int>> dp(disc.size() + 3);
    for (auto p : events) {
      int type = p.second.first;
      int x = p.second.second;
      if (!type) {
        s.erase(x);
      } else {
        auto it = s.insert(x).first;
        for (int i = 0; i < 3; ++i) {
          --it;
        }
        vector<int> all(7);
        for (int i = 0; i < 7; ++i) {
          all[i] = *it;
          ++it;
        }
        for (int i = 0; i < 4; ++i) {
          if (all[i] != -2 && all[i + 3] != disc.size() + 2) {
            adj[all[i + 3]].emplace_back(all[i + 2], make_pair(all[i + 1], all[i]));
          }
        }
      }
    }
    dp[0] = make_pair(0, 1);
    for (int i = 1; i <= disc.size() + 2; ++i) {
      dp[i] = dp[i - 1];
      for (auto p : adj[i]) {
        int x = p.first, y = p.second.first, z = p.second.second;
        dp[i] = max(dp[i], make_pair(1, -x));
        if (y >= 0 && -dp[y].second <= z) {
          dp[i] = max(dp[i], make_pair(dp[y].first + 1, -x));
        }
      }
    }
    printf("Case #%d: %d\n", ttt, dp[disc.size() + 2].first);
  }
  return 0;
}

City Lights

建出关于星星的笛卡尔树,对于建筑物的限制,相当于树上一条链必须选至少一个。这个问题可以贪心做,把贪心的过程加上DP计数即可。

#include <bits/stdc++.h>

using namespace std;

const int md = 1e9 + 7;

inline void add(int &x, int y) {
  x += y;
  if (x >= md) {
    x -= md;
  }
}

inline int mul(int x, int y) {
  return (long long)x * y % md;
}

int main() {
  int tt;
  cin >> tt;
  for (int ttt = 1; ttt <= tt; ++ttt) {
    int n, m;
    cin >> n >> m;
    vector<int> xn(n), yn(n), xm(m), ym(m);
    vector<int> disc;
    disc.push_back(0);
    for (int i = 0; i < n; ++i) {
      cin >> xn[i] >> yn[i];
      disc.push_back(yn[i]);
    }
    for (int i = 0; i < m; ++i) {
      cin >> xm[i] >> ym[i];
      disc.push_back(ym[i]);
    }
    sort(disc.begin(), disc.end());
    disc.erase(unique(disc.begin(), disc.end()), disc.end());
    for (int i = 0; i < n; ++i) {
      yn[i] = lower_bound(disc.begin(), disc.end(), yn[i]) - disc.begin();
    }
    for (int i = 0; i < m; ++i) {
      ym[i] = lower_bound(disc.begin(), disc.end(), ym[i]) - disc.begin();
    }
    vector<int> p(m);
    for (int i = 0; i < m; ++i) {
      p[i] = i;
    }
    sort(p.begin(), p.end(), [&](const int &a, const int &b) {
      return ym[a] < ym[b];
    });
    set<pair<pair<int, int>, int>> s;
    vector<vector<int>> adj(m << 1 | 1);
    vector<int> height(m << 1 | 1);
    int total = 0;
    s.insert(make_pair(make_pair(0, 1 << 30), total));
    height[total++] = 1;
    map<int, int> id;
    for (auto i : p) {
      int x = xm[i], y = ym[i];
      auto it = --s.lower_bound(make_pair(make_pair(x + 1, 0), 0));
      int left = it->first.first, right = it->first.second, index = it->second;
      if (left <= x && right >= x) {
        if (left < x) {
          adj[index].push_back(total);
          s.insert(make_pair(make_pair(left, x - 1), total));
          height[total++] = y;
        }
        if (right > x) {
          adj[index].push_back(total);
          s.insert(make_pair(make_pair(x + 1, right), total));
          height[total++] = y;
        }
        s.erase(it);
        id[x] = index;
      }
    }
    vector<vector<int>> event(total);
    for (int i = 0; i < n; ++i) {
      int x = xn[i], y = yn[i];
      if (id.find(x) != id.end()) {
        event[id[x]].push_back(y);
      } else {
        auto it = --s.lower_bound(make_pair(make_pair(x + 1, 1), 0));
        event[it->second].push_back(y);
      }
    }
    vector<vector<vector<int>>> dp(total, vector<vector<int>> (disc.size(), vector<int> (n + 1)));
    vector<vector<vector<int>>> sum(total, vector<vector<int>> (disc.size() + 1, vector<int> (n + 1)));
    vector<int> binary(n + 1);
    binary[0] = 1;
    for (int i = 0; i < n; ++i) {
      binary[i + 1] = mul(binary[i], 2);
    }
    auto init = [&](int x) {
      for (int i = 0; i < disc.size(); ++i) {
        for (int j = 0; j <= n; ++j) {
          sum[x][i + 1][j] = sum[x][i][j];
          add(sum[x][i + 1][j], dp[x][i][j]);
        }
      }
    };
    function<void(int)> dfs = [&](int x) {
      dp[x][0][0] = 1;
      for (auto y : adj[x]) {
        dfs(y);
        init(x);
        init(y);
        vector<vector<int>> temp(disc.size(), vector<int> (n + 1));
        for (int i = 0; i < disc.size(); ++i) {
          for (int j = 0; j <= n; ++j) {
            for (int k = 0; j + k <= n; ++k) {
              add(temp[i][j + k], mul(dp[x][i][j], sum[y][i + 1][k]));
              add(temp[i][j + k], mul(sum[x][i][j], dp[y][i][k]));
            }
          }
        }
        dp[x] = temp;
      }
      sort(event[x].begin(), event[x].end(), greater<int> ());
      vector<vector<int>> temp(disc.size(), vector<int> (n + 1));
      for (int i = 0; i <= event[x].size(); ++i) {
        int value = i == event[x].size() ? 0 : event[x][i];
        int ways = i == event[x].size() ? 1 : binary[event[x].size() - i - 1];
        for (int j = 0; j < disc.size(); ++j) {
          for (int k = 0; k <= n; ++k) {
            add(temp[max(j, value)][k], mul(dp[x][j][k], ways));
          }
        }
      }
      dp[x] = temp;
      for (int i = height[x]; i < disc.size(); ++i) {
        for (int j = 0; j < n; ++j) {
          add(dp[x][0][j + 1], dp[x][i][j]);
          dp[x][i][j] = 0;
        }
      }
    };
    dfs(0);
    int answer = 0;
    for (int i = 1; i <= n; ++i) {
      add(answer, mul(i, dp[0][0][i]));
    }
    cout << "Case #" << ttt << ": " << answer << endl;
  }
  return 0;
}

The Claw

h i h_i hi 表示 x i x_i xi x i + 1 x_{i+1} xi+1 之间 y y y 的最大值,那么 a n s = 2 ( m + ∑ h i − ∑ y i ) ans = 2(m + \sum h_i - \sum y_i) ans=2(m+hiyi) h i h_i hi 改变当且仅当区间内有一个是区间最大值的往上提升了 1 1 1 。那么不难发现不同 y i y_i yi 之间的贡献是独立的,用线段树优化DP即可。

#include <bits/stdc++.h>

using namespace std;

class segtree_t {
 public:
  struct node_t {
    int tag = 0, value = 0;
    
    void apply(int l, int r, int v) {
      tag += v;
      value += v;
    }
  };
  
  vector<node_t> tree;
  int n;

  node_t unite(const node_t &l, const node_t &r) {
    node_t result;
    result.value = max(l.value, r.value);
    return result;
  }

  segtree_t(int n):n(n) {
    tree.resize((n << 1) - 1);
  }

  inline void pull(int x, int z) {
    tree[x] = unite(tree[x + 1], tree[z]);
  }

  inline void push(int x, int l, int r) {
    int y = l + r >> 1, z = x + (y - l + 1 << 1);
    if (tree[x].tag) {
      tree[x + 1].apply(l, y, tree[x].tag);
      tree[z].apply(y + 1, r, tree[x].tag);
      tree[x].tag = 0;
    }
  }

  void build(int x, int l, int r) {
    if (l != r) {
      int y = l + r >> 1, z = x + (y - l + 1 << 1);
      build(x + 1, l, y);
      build(z, y + 1, r);
      pull(x, z);
    }
  }

  template<typename T> void build(int x, int l, int r, const vector<T> &v) {
    if (l == r) {
      tree[x].apply(l, r, v[l]);
    } else {
      int y = l + r >> 1, z = x + (y - l + 1 << 1);
      build(x + 1, l, y);
      build(z, y + 1, r);
      pull(x, z);
    }
  }

  template<typename... T> void modify(int x, int l, int r, int ql, int qr, const T&... v) {
    if (l == ql && r == qr) {
      tree[x].apply(l, r, v...);
    } else {
      int y = l + r >> 1, z = x + (y - l + 1 << 1);
      push(x, l, r);
      if (qr <= y) {
        modify(x + 1, l, y, ql, qr, v...);
      } else if (ql > y) {
        modify(z, y + 1, r, ql, qr, v...);
      } else {
        modify(x + 1, l, y, ql, y, v...);
        modify(z, y + 1, r, y + 1, qr, v...);
      }
      pull(x, z);
    }
  }

  template<typename... T> void modify(int l, int r, const T&... v) {
    modify(0, 0, n - 1, l, r, v...);
  }

  node_t query(int x, int l, int r, int ql, int qr) {
    if (l == ql && r == qr) {
      return tree[x];
    } else {
      int y = l + r >> 1, z = x + (y - l + 1 << 1);
      push(x, l, r);
      if (qr <= y) {
        return query(x + 1, l, y, ql, qr);
      } else if (ql > y) {
        return query(z, y + 1, r, ql, qr);
      } else {
        return unite(query(x + 1, l, y, ql, y), query(z, y + 1, r, y + 1, qr));
      }
    }
  }

  node_t query(int l, int r) {
    return query(0, 0, n - 1, l, r);
  }
};

int main() {
  int tt;
  scanf("%d", &tt);
  for (int ttt = 1; ttt <= tt; ++ttt) {
    int n, m;
    scanf("%d %d", &n, &m);
    vector<int> x(n), y(n);
    vector<vector<int>> nodes(m);
    vector<vector<pair<int, int>>> intervals(m);
    for (int i = 0; i < n; ++i) {
      scanf("%d %d", &x[i], &y[i]);
      nodes[y[i]].push_back(x[i]);
    }
    long long answer = m;
    int range = *max_element(x.begin(), x.end()) + 1;
    int log_range = 0;
    while (1 << log_range < range) {
      ++log_range;
    }
    vector<vector<int>> rmq(log_range, vector<int> (range));
    for (int i = 0; i < n; ++i) {
      rmq[0][x[i]] = max(rmq[0][x[i]], y[i]);
    }
    for (int i = 1; i < log_range; ++i) {
      for (int j = 0; j + (1 << i) <= range; ++j) {
        rmq[i][j] = max(rmq[i - 1][j], rmq[i - 1][j + (1 << i - 1)]);
      }
    }
    vector<int> rmq_length(range);
    for (int i = 2; i < range; ++i) {
      rmq_length[i] = rmq_length[i >> 1] + 1;
    }
    auto query_max = [&](int l, int r) {
      int k = rmq_length[r - l];
      return max(rmq[k][l], rmq[k][r - (1 << k) + 1]);
    };
    for (int i = 0; i < n - 1; ++i) {
      int l = min(x[i], x[i + 1]), r = max(x[i], x[i + 1]);
      if (l > r) {
        swap(l, r);
      }
      int height = query_max(l, r);
      answer += height;
      intervals[height].emplace_back(l, r);
    }
    for (int i = 0; i < n; ++i) {
      answer -= y[i];
    }
    for (int i = 1; i < m; ++i) {
      if (!nodes[i].empty()) {
        sort(nodes[i].begin(), nodes[i].end());
        vector<vector<int>> events(nodes[i].size());
        for (auto &p : intervals[i]) {
          p.first = lower_bound(nodes[i].begin(), nodes[i].end(), p.first) - nodes[i].begin();
          p.second = upper_bound(nodes[i].begin(), nodes[i].end(), p.second) - nodes[i].begin() - 1;
          if (p.first <= p.second) {
            events[p.second].push_back(p.first);
            ++answer;
          }
        }
        segtree_t segtree(nodes[i].size() + 1);
        for (int j = 0; j < nodes[i].size(); ++j) {
          segtree.modify(j + 1, j + 1, segtree.query(0, j).value + 1);
          for (auto p : events[j]) {
            segtree.modify(0, p, 1);
          }
        }
        answer -= segtree.query(0, nodes[i].size()).value;
      }
    }
    answer <<= 1;
    printf("Case #%d: %lld\n", ttt, answer);
  }
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值