Codeforces Round 881 (Div. 3)A~E

目录

Dashboard - Codeforces Round 881 (Div. 3) - Codeforces

A. Sasha and Array Coloring

B. Long Long

C. Sum in Binary Tree

D. Apple Tree

E. Tracking Segments(补)


本家:

Dashboard - Codeforces Round 881 (Div. 3) - Codeforces

A. Sasha and Array Coloring

题意:对给定数组进行分类,每一类的价值为该类中最大值减去最小值,总数组的价值为各类价值之和,求最大价值

题解:对数组排序后,从数组两端向中间靠拢进行遍历,每次循环加上两端相减的值即可,比较符合直觉的贪心题

#include <bits/stdc++.h>
#define sep ' '
#define ent '\n'
using namespace std;
using ll = long long;

void solve() {
  int t;
  cin >> t;
  while (t--) {
    int n;
    cin >> n;
    vector<int> a(n);
    for (auto& i : a)
      cin >> i;
    sort(a.begin(), a.end());
    ll ans = 0;
    for (int i = 0; i < n / 2; ++i) {
      ans += a[n - i - 1] - a[i];
    }
    cout << ans << ent;
  }
}

int main() {
  ios::sync_with_stdio(0);
  cin.tie(0);
  solve();
  return 0;
}

B. Long Long

题意:给定一个数组,你可以选取一个区间[ l , r ],对区间内所有元素的符号取反,问最少进行多少次这样的操作使得数组元素和最大

题解:输入时遇到0就跳过,只把非0元素加入vector内,然后记录有几个单独的负数区间即可。当几个负数区间之间夹杂着正数区间时全部进行翻转并不会使答案更优。不那么符合直觉的贪心题

#include <bits/stdc++.h>
#define sep ' '
#define ent '\n'
using namespace std;
using ll = long long;

void solve() {
  int t;
  cin >> t;
  while (t--) {
    int n;
    cin >> n;
    vector<int> a;
    for (int i = 0; i < n; ++i) {
      int tmp;
      cin >> tmp;
      if (tmp != 0)
        a.push_back(tmp);
    }
    n = a.size();
    ll ans = 0, cnt = 0;
    bool c = 1;
    for (int i = 0; i < n; ++i) {
      int tmp;
      tmp = a[i];
      ans += abs(tmp);
      if (c && tmp <= 0) {
        c = 0;
        ++cnt;
      }
      if (tmp > 0)
        c = 1;
    }
    cout << ans << sep << cnt << ent;
  }
}

int main() {
  ios::sync_with_stdio(0);
  cin.tie(0);
  solve();
  return 0;
}

C. Sum in Binary Tree

题意:给定一个有n个节点的二叉树,每个节点的值为自身编号,左右子节点 c1、c2 和父节点 f 关系满足 f=c1/2、f=(c2-1)/2,根节点为1,求从1到n的路径上的节点值的和(包含1和n)

题解:考虑二叉树的性质,从n节点出发,只要每次加上当前节点值后对其除2,循环直到当前节点为0退出循环即可,感觉比B题简单

#include <bits/stdc++.h>
#define sep ' '
#define ent '\n'
using namespace std;
using ll = long long;

void solve() {
  int t;
  cin >> t;
  while (t--) {
    ll n;
    cin >> n;
    ll ans = 0;
    while (n) {
      ans += n;
      n >>= 1;
    }
    cout << ans << ent;
  }
}

int main() {
  ios::sync_with_stdio(0);
  cin.tie(0);
  solve();
  return 0;
}

D. Apple Tree

题意:给定一棵n个节点的树,根为1,树上某两个节点各有一个苹果(这两个节点可以相同),每次摇树就会使苹果向子节点移动,若有多个子节点则随机向某个子节点移动,无子节点时从树上掉落下来,记录两个苹果掉落的子节点位置(u,v)(掉落位置可以相同)。给定两个苹果所在节点位置,问有多少种掉落方式?

题解:用cnt数组记录每个节点所在子树所含有的叶子节点数,每次询问时直接将两个节点所对应的cnt值乘起来输出即可。如果当前节点有苹果,其掉落可能的数量只可能是 该节点作为根 所在的子树的叶子节点数,另外一个苹果也是如此,然后两者相乘即为所有可能的结果数

代码解析:用了链式前向星(本质其实就是邻接表)来存边。dfs的思路是先递归到叶子节点,更新叶子节点的cnt值后返回,父节点加上叶子节点值后,再依次返回更新父节点的父节点。注意叶子节点的判断即可

#include <bits/stdc++.h>
#define sep ' '
#define ent '\n'
using namespace std;
using ll = long long;
const int maxn = 2e5 + 1;

int head[maxn], ecnt;
struct edge {
  int v, next;
} e[2 * maxn];

void add(int u, int v) {
  e[++ecnt] = {v, head[u]};
  head[u] = ecnt;
}

int cnt[maxn];

void dfs(int u, int fa) {
  if (e[head[u]].v == fa && e[head[u]].next == 0) {
    cnt[u] = 1;
    return;
  }
  for (int i = head[u]; i; i = e[i].next) {
    int v = e[i].v;
    if (v == fa)
      continue;
    dfs(v, u);
    cnt[u] += cnt[v];
  }
}

void solve() {
  int t;
  cin >> t;
  while (t--) {
    int n;
    cin >> n;
    memset(head, 0, sizeof(int) * (n + 1));
    memset(cnt, 0, sizeof(int) * (n + 1));
    ecnt = 0;
    for (int i = 1; i < n; ++i) {
      int u, v;
      cin >> u >> v;
      add(u, v);
      add(v, u);
    }
    dfs(1, 0);
    int q;
    cin >> q;
    while (q--) {
      int u, v;
      cin >> u >> v;
      cout << 1ll * cnt[u] * cnt[v] << ent;
    }
  }
}

int main() {
  ios::sync_with_stdio(0);
  cin.tie(0);
  solve();
  return 0;
}

E. Tracking Segments(补)

题意:给定一个大小为n,元素全为0的数组a;再给定m个区间,若一个区间中1的数量严格大于0的数量则视为满足要求;最后给定q次操作,每次操作将给定下标的元素值变为1。问哪次操作可以最早使至少一个区间满足要求

题解:二分枚举操作的次数,并将该状态下的数组a用前缀和处理出来,然后遍历m个区间,判断是否有任意一个满足要求。一道单调性不那么好想的二分答案加前缀和的题

#include <bits/stdc++.h>
#define sep ' '
#define ent '\n'
using namespace std;
using ll = long long;

void solve() {
  int t;
  cin >> t;
  while (t--) {
    int n, m;
    cin >> n >> m;
    using pii = pair<int, int>;
    vector<pii> a(m);
    for (auto& i : a)
      cin >> i.first >> i.second;
    int q;
    cin >> q;
    //储存操作序列
    vector<int> op(q + 1);
    for (int i = 1; i <= q; ++i)
      cin >> op[i];

    //储存每次操作状态的01串的前缀和
    vector<int> bi(n + 1);
    //lambda表达式
    auto check = [&](int x) {
      fill(bi.begin(), bi.end(), 0);
      for (int i = 1; i <= x; ++i)
        bi[op[i]] = 1;
      for (int i = 1; i <= n; ++i)
        bi[i] += bi[i - 1];
      for (auto i : a) {
        int l = i.first, r = i.second;
        if (bi[r] - bi[l - 1] >= (r - l + 1) / 2 + 1)
          return true;
      }
      return false;
    };
    int l = 1, r = q + 1;
    while (l < r) {
      int mid = l + r >> 1;
      if (check(mid))
        r = mid;
      else
        l = mid + 1;
    }
    cout << (l == q + 1 ? -1 : l) << ent;
  }
}

int main() {
  ios::sync_with_stdio(0);
  cin.tie(0);
  solve();
  return 0;
}

总结:本轮D题卡的过久了,还是对邻接表和dfs理解不深;E题赛时思路混乱。继续努力

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值