• 欢迎光临~

CF #575 Div3(A_F)

开发技术 开发技术 2022-10-09 次浏览

A. Three Piles of Candies

题意:

给三个堆蛋糕,(Alice)(Bob) 两个人分,要使得两人最后拿到手上的蛋糕数一样多,求两人最多可以获得多少蛋糕。

    void solve() {
        i64 a, b, c;
        std::cin >> a >> b >> c;
        std::cout << (a + b + c) / 2 << "n";
    }

B. Odd Sum Segments

题意:

将一个数组分成连续的 (k) 段,使得每一段内各个数字之和是一个奇数。如果存在这种分法,输出 (k) 个区间的右端点,否则输出 (No)

思路:

根据奇数 + 奇数 = 偶数, 偶数 + 偶数 = 偶数奇数 + 偶数 = 奇数可知,要尽可能的让分出的区间数量多,就将每一个奇数分布在不同的区间内,这样奇数和奇数所产生的贡献就不会互相抵消掉。那么判断能不能分出 (k) 段区间,就是看这个区间内有多少个奇数。现在考虑分法,如果奇数的个数刚好等于 (k) 那么直接输出就好了;如果奇数个数大于 (k) ,就要考虑区间合并的问题了,因为 奇数+奇数 = 奇数,所以每一次如果只是两两合并的话,会将两个奇数变成偶数,导致现在这一段不满足条件,所以每一次合并的时候就是三个区间合并成为一个区间,也就相当于是每一次从最后面减掉两个区间,很明显任何一个数减去一个偶数不会改变它本身的奇偶性,所以在判断是否能够分出的时候,还要去考虑奇数的个数的奇偶性是不是和 (k) 的奇偶性是相同的。

    void solve() {
        int n, m;
        std::cin >> n >> m;
        std::vector<int> a(n + 1);
        i64 ans = 0;
        std::vector<int> res;
        for (int i = 1; i <= n; i++) {
            std::cin >> a[i];
            ans += a[i];
            if (ans & 1) {
                res.push_back(i);
                ans = 0;
            }
        }

        if (int(res.size()) % 2 == m % 2 && res.size() >= m) {
            std::cout << "Yesn";
            while(res.size() > m) res.pop_back();
            res.back() = n;
            for (int i = 0; i < m; i++) std::cout << res[i] << " n"[i == m - 1];
        } else {
            std::cout << "Non";
        }
    }

C.Robot Breakout

题意:

(n) 个机器人在一个平面上,第 (i) 个机器人的位置是 ((X_i,Y_i))。在设计的时候,第 (i) 个机器人可以执行的操作:

  1. 位置从 ((X_i,Y_i)) 变为 ((X_i-1,Y_i))
  2. 位置从 ((X_i,Y_i)) 变为 ((X_i,Y_i+1))
  3. 位置从 ((X_i,Y_i)) 变为 ((X_i+1,Y_i))
  4. 位置从 ((X_i,Y_i)) 变为 ((X_i,Y_i-1))

但设计出现了缺陷,某些机器人可能不能执行上述的某些操作。你需要找一个点 ((A,H)),使得 (n) 个机器人都可以到达 ((A,H)) 。注意,一开始的位置在 ((A,H)) 也算到达,且对于 (A,H) 的范围有限制 —— (-10^5leq A,H leq 10^5)

思路:

对每一个功能进行分析,功能一缺失意味着这个机器人不能向左走,功能二缺失意味着机器人不能向上走,功能三缺失意味着这个机器人不能向右走,功能四缺失意味着这个机器人不能向下走。那么我们就可以根据机器人现在的位置来确定所能够移动的左右边界和上下边界。功能一来确定左边界,功能二来确定上边界,功能三来确定右边界,功能四来确定下边界。

   void solve() {
       int n; std::cin >> n;

       int x = -1E5, y = -1E5, xx = 1E5, yy = 1E5;
       for (int i = 0; i < n; i++) {
           int sx, sy, a, b, c, d;
           std::cin >> sx >> sy >> a >> b >> c >> d;
           if (!a) x = std::max(x, sx);
           if (!b) yy = std::min(yy, sy);
           if (!c) xx = std::min(xx, sx);
           if (!d) y = std::max(y, sy);
       }

       if (xx < x || yy < y) std::cout << 0 << "n";
       else {
           std::cout << "1 " << x << " " << y << "n";
       }

   }

D2.RGB Substring (hard version)

题意:

求修改字符串 (s) 多少次能够让其中的某一个子串(长度为 (k) )成为 (RGBRGBdots RGB) 的子串。

思路:

(D1) 的话可以考虑爆力枚举出所有的子串来分别计算修改得次数。 (D2) 的话就需要去考虑优化的方案了。因为要求一个区间内修改次数的多少,而对于快速求区间和,不难想到用前缀和来优化这个过程。可以考虑用一个目标串 (RGB) 来计算当前位置需不需要修改,如果需要修改就在上一个位置的基础上加一。

    std::string aim = "RGB";
    std::vector<std::vector<int>> dp(3, std::vector<int> (n + 1));
 
    for (int i = 0; i < n; i++) 
        for (int j = 0; j < 3; j++) 
            dp[j][i + 1] = dp[j][i] + (s[i] == aim[(i + j) % 3] ? 0 : 1); 
            // 如果匹配上了,就不需要修改,否则修改次数 + 1

最后就是尺取出所有长度为 (k) 的子串然后计算修改次数

   void solve() {
       int n, k;
       std::cin >> n >> k;
       std::string s; std::cin >> s;
       std::string aim = "RGB";
       std::vector<std::vector<int>> dp(3, std::vector<int> (n + 1));

       for (int i = 0; i < n; i++) 
           for (int j = 0; j < 3; j++) 
               dp[j][i + 1] = dp[j][i] + (s[i] == aim[(i + j) % 3] ? 0 : 1);

       int ans = k;
       for (int i = 0; i <= n - k; i++) 
           for (int j = 0; j <= 2; j++) 
               ans = std::min(ans, dp[j][i + k] - dp[j][i]);
       std::cout << ans << "n";
   }

E.Connected Component on a Chessboard

题意:

给出一个 (10^9 × 10 ^ 9) 的黑白棋盘( (i + j) 为奇数的时候是白色,否则为黑色),让我们构造出一个连通块使得黑色的点有 (b) 个,白色的点有 (w) 个,并输出构造方案。

思路:

画图可以发现,要想构成一个联通块的话,就必须要让所有的黑白点相邻,而且最优的情况就是尽量使得黑色/白色点的上下左右都选上。那么就考虑将最少的颜色 (a) 的点放在一条直线上,另一个颜色 (b) 插空,这样把 (a) 放置好了,就只需要去考虑将剩下的 (b) 都放置在 (a) 的上下方。

   void solve() {
       int b, w;
       std::cin >> b >> w;
       bool f = w > b;
       if (f) std::swap(w, b);

       std::vector<std::array<int, 2>> res;
       int x = 2, y = 2;
       while(w) {
           res.push_back({x, y});
           b -= (y & 1);
           w -= !(y & 1);
           y++;
       }

       int xx = 1, yy = 2;
       while(b && yy <= y) {
           res.push_back({xx, yy});
           b--, yy += 2;
       }
       int px = 3, py = 2;
       while(b && py <= y) {
           res.push_back({px, py});
           b--, py += 2;
       }

       if (b) res.push_back({2, 1}), b--;
       if (b) res.push_back({2, y}), b--;
       if (b) std::cout << "NOn";
       else {
           std::cout << "YESn";
           for (auto& now : res) 
            std::cout << now[0] << " " << now[1] + f << "n";
       }
   }

F.K-th Path

题意:

给定一个无向带权连通图,求子节点两两之间路径从小到大排序之后第 (k) 长的路径长度。

思路:

首先,注意到 (k) 非常的小,如果只考虑边,那么最多有 (k) 条边会对答案产生影响,也就是说答案一定小于等于第 (k) 小的那条边。既然如此,对答案有影响的路径肯定也是由 (k) 条路径相互组合产生的,那么我们就知道了对答案有影响的点最多只有 (k) 级别的。那我们就只加前 (k) 小的边,然后对所有的被加过边的点跑最短路,然后用这个点到其他点的最短路去更新答案

      constexpr int N = 200010, M = N * 2;
          
      struct node {
          int v, w, nxt;
      }e[M];

      int idx, n, m, k, cnt;
      int h[N], a[N];
      i64 dist[N];
      bool vis[N], st[N];

      void add(int a, int b, int c) {
          e[++idx] = {b, c, h[a]}, h[a] = idx;
      }

      struct line {
          int u, v, w;
          bool operator< (const line& l) const {
              return w < l.w;
          }
      }edge[N];

      void dijkstra(int s) {
          for (int i = 1; i <= cnt; i++) {
              dist[a[i]] = 1E18;
              vis[a[i]] = false;
          }

          dist[s] = 0;
          std::priority_queue<pii, std::vector<pii>, std::greater<pii>> q;
          q.push({0, s});
          while(q.size()) {
              auto p = q.top(); q.pop();
              int u = p.second;

              if (vis[u]) continue;
              vis[u] = true;

              for (int i = h[u]; i; i = e[i].nxt) {
                  int v = e[i].v, w = e[i].w;
                  if (dist[v] > dist[u] + w) {
                      dist[v] = dist[u] + w;
                      if (!vis[v]) q.push({dist[v], v});
                  }
              }
          }
      }

      signed main() {
          std::cin.tie(nullptr)->sync_with_stdio(false);

          std::cin >> n >> m >> k;
          for (int i = 1; i <= m; i++) {
              int u, v, w;
              std::cin >> u >> v >> w;
              edge[i] = {u, v, w};
          }
          std::sort(edge + 1, edge + 1 + m);

          for (int i = 1; i <= std::min(m, k); i++) {
              add(edge[i].u, edge[i].v, edge[i].w);
              add(edge[i].v, edge[i].u, edge[i].w);

              if (!st[edge[i].u]) a[++cnt] = edge[i].u, st[edge[i].u] = true;
              if (!st[edge[i].v]) a[++cnt] = edge[i].v, st[edge[i].v] = true;
          }

          std::priority_queue<i64> q;

          for (int i = 1; i <= cnt; i++) {
              dijkstra(a[i]);
              for (int j = i + 1; j <= cnt; j++) {
                  q.push(-dist[a[j]]);
              }
          }

          for (int i = 1; i < k; i++) q.pop();
          std::cout << -q.top() << "n";

          return 0 ^ 0;
      }
程序员灯塔
转载请注明原文链接:CF #575 Div3(A_F)
喜欢 (0)
违法和不良信息举报电话:022-22558618 举报邮箱:dljd@tidljd.com