Edu162

Edu162[A-E]

A. Moving Chips[Trick]

功能区分为 n 个单元格,从左到右编号为 1n 。每个单元要么包含一个芯片,要么是免费的。

您可以执行以下操作任意次数(可能为零):选择一个筹码并将其移动到 左侧最近的空闲单元。您可以选择任何您想要的芯片,只要其左侧至少有一个空闲单元即可。当您移动芯片时,操作前所在的单元格就会空闲。

您的目标是以这样的方式移动芯片,使得它们形成一个块,并且它们之间没有任何空闲单元。您必须执行的最少操作次数是多少?


分析

我们找出最左边和最右边的芯片L,R

目标使得[L,R] 之中没有空白格

我们每次移动最右边的芯片,可以填补最右边的空白,并将空出的空白格移到R 右边

这样我们每次移动都可以减少一个空白格的数量

我们的目标是让空白格的数量为0

所需的操作次数就是一开始区间中空白格的数量

code

Submission #247902579 - Codeforces

// Codeforces - Educational Codeforces Round 162 (Rated for Div. 2) A. Moving
// Chips https://codeforces.com/contest/1923/problem/0 2024-02-23 22:35:51

#include <bits/stdc++.h>
using namespace std;
#define all(a) begin(a), end(a)
#define int long long
int n, m;
void solve() {
  cin >> n;
  vector<int> a(n), s(n);
  for (auto &i : a) cin >> i;
  for (int i = 0; i < n; i++)
    if (i)
      s[i] = s[i - 1] + a[i];
    else
      s[i] = a[i];
  int l = 0, r = n - 1;
  while (l < n and a[l] == 0) l++;
  while (r >= 0 and a[r] == 0) r--;
  if (r < l)
    cout << "0\n";
  else if (l)
    cout << -s[r] + s[l - 1] + r - l + 1 << "\n";
  else
    cout << -s[r] + r - l + 1 << "\n";
}

signed main() {
  ios::sync_with_stdio(false);
  cin.tie(0), cout.tie(0);
  int __;
  cin >> __;
  while (__--) solve();
  return 0;
}

B. Monsters Attack![Trick]

您正在玩电脑游戏。该游戏的当前等级可以建模为一条直线。您的角色位于该行的点 0 。有 n 个怪物试图杀死你的角色;第 i 个怪物的生命值等于 ai ,并且最初位于点 xi

每一秒都会发生以下情况:

  • 首先,你向怪物发射最多 k 颗子弹。每颗子弹只瞄准一个怪物并使其生命值降低 1 。对于每颗子弹,您可以任意选择其目标(例如,您可以向一个怪物发射所有子弹,向不同的怪物发射所有子弹,或选择任何其他组合)。任何怪物都可以成为子弹的目标,无论其位置和任何其他因素如何;
  • 然后,所有生命值小于等于 0 的活着的怪物都会死亡;
  • 然后,所有活着的怪物都会向您靠近 1 点(您左边的怪物将其坐标增加 1 ,您右侧的怪物将其坐标减少 1 )。如果任何怪物到达你的角色(移动到点 0 ),你就输了。

您能生存并杀死所有 n 个怪物而不让它们接触到您的角色吗?

分析

这个题也是一个小Trick

我们维护前缀 X 秒能不能 消灭所有 前 X 秒会靠近主角的怪兽就好

如果坐标为负数我们就取绝对值 转移到正数 方便处理

code

Submission #247906784 - Codeforces

// Codeforces - Educational Codeforces Round 162 (Rated for Div. 2) B. Monsters
// Attack! https://codeforces.com/contest/1923/problem/B 2024-02-23 22:47:36

#include <bits/stdc++.h>
using namespace std;
#define all(a) begin(a), end(a)
#define int long long
int n, k;
void solve() {
  cin >> n >> k;
  vector<int> a(n), x(n);
  for (auto &i : a) cin >> i;
  for (auto &i : x) cin >> i;
  map<int, int> mp;
  for (int i = 0; i < n; i++)
    if (x[i] < 0)
      mp[-x[i]] += a[i];
    else
      mp[x[i]] += a[i];
  int res = 0;
  for (auto [v, w] : mp)
    if (res + w <= v * k)
      res += w;
    else
      return (void)(cout << "NO\n");
  cout << "YES\n";
}

signed main() {
  ios::sync_with_stdio(false);
  cin.tie(0), cout.tie(0);
  int __;
  cin >> __;
  while (__--) solve();
  return 0;
}

C. Find B [贪心]

如果存在长度为 m 的数组 b ,并且满足以下条件,则长度为 m 的数组 a 被认为是良好的:

  1. i=1mai=i=1mbi
  2. aibi 对于从 1m 的每个索引 i
  3. bi>0 对于从 1m 的每个索引 i

给您一个长度为 n 的数组 c 。该数组的每个元素都大于 0

您必须回答 q 个查询。在第 i 个查询期间,您必须确定子数组 cli,cli+1,,cri 是否良好。

分析

对于一个数组

如果存在相同长度的另一个数组

使得两个数组之和相同

另一个数组每个位置都与当前数组的不同

而且另一个数组的每个位置都是大于0 的

贪心的来想

就让之前大于1 的地方全都是1

把之前是1的地方都填上2

剩余量大于等于0就行

code

Submission #247922876 - Codeforces

// Codeforces - Educational Codeforces Round 162 (Rated for Div. 2) C. Find B
// https://codeforces.com/contest/1923/problem/C 2024-02-23 22:52:06

#include <bits/stdc++.h>
using namespace std;
#define all(a) begin(a), end(a)
#define int long long
int n, q;
void solve() {
  cin >> n >> q;
  vector<int> c(n + 1), s(n + 1), cnt1(n + 1);
  for (int i = 1; i <= n; i++) cin >> c[i];
  for (int i = 1; i <= n; i++) s[i] = s[i - 1] + c[i];
  for (int i = 1; i <= n; i++) cnt1[i] = cnt1[i - 1] + (c[i] == 1);
  while (q--) {
    int l, r;
    cin >> l >> r;
    if (l == r) {
      cout << "NO\n";
      continue;
    }
    int res = s[r] - s[l - 1] - cnt1[r] + cnt1[l - 1] - r + l - 1;
    if (res >= 0)
      cout << "YES\n";
    else
      cout << "NO\n";
  }
}
signed main() {
  ios::sync_with_stdio(false);
  cin.tie(0), cout.tie(0);
  int __;
  cin >> __;
  while (__--) solve();
  return 0;
}

D. Slimes[二分]

n 个史莱姆排成一行。史莱姆按从左到右的顺序编号为 1n 。第 i 个史莱姆的大小为 ai

每一秒,都会发生以下情况:恰好一个史莱姆会吃掉它的邻居之一,并根据被吃掉的邻居的大小来增加其大小。只有当史莱姆严格大于邻居时,它才能吃掉它的邻居。如果没有严格大于其邻居之一的史莱姆,则该过程结束。

例如,假设 n=5a=[2,2,3,1,4] 。该过程可以如下进行:

  • 首先,第 3 个史莱姆吃掉了第 2 个史莱姆。第 3 个史莱姆的大小变为 5 ,第 2 个史莱姆被吃掉。
  • 然后, 3 -rd 史莱姆吃掉 1 -st 史莱姆(它们是邻居,因为 2 -nd 史莱姆已经被吃掉了)。 3 -rd 史莱姆的大小变为 71 -st 史莱姆被吃掉。
  • 然后,第 5 个史莱姆吃掉第 4 个史莱姆。第 5 个史莱姆的大小变成了 5 ,第 4 个史莱姆被吃掉。
  • 然后,第 3 个史莱姆吃掉第 5 个史莱姆(它们是邻居,因为第 4 个史莱姆已经被吃掉了)。第 3 个史莱姆的大小变成了 12 ,第 5 个史莱姆被吃掉。

对于每个史莱姆,计算该史莱姆被另一个史莱姆吃掉所需的最短秒数(在该过程的所有可能方式中),或者报告这是不可能的。

分析

先处理能否在一开始被身边的吃掉

找到最近的未被吃掉的

否则的话就二分离当前位置的距离

由于相同大小的 是不能互相吞噬的

所以我们把大小看成颜色

维护每个颜色的左右端点

二分的时候跳过相邻的颜色段

当有两个及以上的颜色段,这两段区间便可以合成一个很大的 怪物

code

Submission #247951109 - Codeforces

// Codeforces - Educational Codeforces Round 162 (Rated for Div. 2) D. Slimes
// https://codeforces.com/contest/1923/problem/D 2024-02-23 23:18:47

#include <bits/stdc++.h>
using namespace std;
#define all(a) begin(a), end(a)
#define int long long
int n, m;
void solve() {
  cin >> n;
  vector<int> a(n + 1), L(n + 1), R(n + 1);
  for (int i = 1; i <= n; i++) cin >> a[i];
  vector<int> ans(n + 1, n + 1);
  for (int i = 1; i <= n; i++) {
    if (i < n)
      if (a[i + 1] > a[i]) ans[i] = 1;
    if (i > 1)
      if (a[i - 1] > a[i]) ans[i] = 1;
  }
  for (int i = 1; i <= n; i++) L[i] = R[i] = i;
  for (int i = 2; i <= n; i++)
    if (a[i] == a[i - 1]) L[i] = L[i - 1];
  for (int i = n - 1; i >= 1; i--)
    if (a[i] == a[i + 1]) R[i] = R[i + 1];
  vector<int> s(n + 1);
  for (int i = 1; i <= n; i++) s[i] = s[i - 1] + a[i];
  for (int i = 1; i <= n; i++) {
    if (ans[i] == n + 1) {
      if (i < n) {
        int l = R[i + 1] + 1LL, r = n;
        while (l <= r) {
          int mid = l + r >> 1;
          if (s[mid] - s[i] > a[i])
            ans[i] = min(ans[i], mid - i), r = mid - 1;
          else
            l = mid + 1;
        }
      }
      if (i > 1) {
        int l = 1, r = L[i - 1] - 1LL;
        while (l <= r) {
          int mid = l + r >> 1;
          if (s[i - 1] - s[mid - 1] > a[i])
            ans[i] = min(ans[i], i - mid), l = mid + 1;
          else
            r = mid - 1;
        }
      }
    }
  }
  for (auto &i : ans)
    if (i == n + 1) i = -1;
  for (int i = 1; i <= n; i++) cout << ans[i] << " \n"[i == n];
}
signed main() {
  ios::sync_with_stdio(false);
  cin.tie(0), cout.tie(0);
  int __;
  cin >> __;
  while (__--) solve();
  return 0;
}

E. Count Paths[启发式合并]

给您一棵树,由 n 个顶点组成,编号从 1n 。每个顶点都涂有某种颜色,用从 1n 的整数表示。

如果满足以下条件,则树的简单路径称为美丽路径:

  • 它至少由 2 个顶点组成;
  • 路径的第一个和最后一个顶点具有相同的颜色;
  • 路径上没有其他顶点与第一个顶点具有相同的颜色。

数数树上美丽简单的路径有多少条。请注意,路径被视为无向(即从 xy 的路径与从 yx 的路径相同)。

分析

我们可以知道答案有两种

一种是父子链上的选择

另一种是一个节点的不同孩子之间

对于第一种我们需要维护 子树中有没有这个父亲的颜色

对于第二种我们需要维护 子树中有多少链有 相同的颜色

于是我们可以用map<int,int>来维护每一种颜色

但是直接维护是O(n2)的显然不可以接受

但是有一种奇妙的东西叫做启发式合并

简而言之就是在合并信息的时候,将size较小的合并到size较大的中

可以发现每一次合并对于任意一个移动的点,所在的区间的大小都会扩增至少2

所以可以在O(log2n)次移动中完成维护

时间复杂度的上界是O(nlog2n)

code

codeforces.com/contest/1923/submission/248062684

// Codeforces - Educational Codeforces Round 162 (Rated for Div. 2) E. Count
// Paths https://codeforces.com/contest/1923/problem/E 2024-02-23 23:57:48

#include <bits/stdc++.h>
using namespace std;
#define all(a) begin(a), end(a)
#define int long long
int n, m;
void solve() {
  cin >> n;
  vector<int> col(n);
  for (auto &i : col) cin >> i;
  vector<vector<int>> G(n);
  for (int i = 1; i < n; i++) {
    int x, y;
    cin >> x >> y;
    x--, y--;
    G[x].emplace_back(y);
    G[y].emplace_back(x);
  }
  vector<map<int, int>> mp(n);
  int ans = 0;
  auto merge = [&](int i, int j) -> void {
    if (mp[i].size() < mp[j].size()) swap(mp[i], mp[j]);
    for (auto [x, y] : mp[j]) mp[i][x] += y;
  };
  auto dfs = [&](auto self, int u, int fa) -> void {
    mp[u][col[u]]++;
    for (auto it : G[u]) {
      if (it == fa) continue;
      self(self, it, u);
      if (mp[it].contains(col[u]))
        ans += (mp[it][col[u]] * mp[it][col[u]] - mp[it][col[u]]) / 2LL;
      merge(u, it);
    }
    ans += mp[u][col[u]] - 1;
    mp[u][col[u]] = 1;
  };
  dfs(dfs, 0, 0);
  for (auto [x, y] : mp[0]) ans += (y * y - y) / 2LL;
  cout << ans << "\n";
}

signed main() {
  ios::sync_with_stdio(false);
  cin.tie(0), cout.tie(0);
  int __;
  cin >> __;
  while (__--) solve();
  return 0;
}

本文作者:liangqianxing

本文链接:https://www.cnblogs.com/liangqianxing/p/18031876

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   liangqianxing  阅读(49)  评论(1编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起