牛客小白月赛30

导读:
简单的题目,只说明题意,或者直接说明结论
下面的难题,会做详细的解释和证明

A 黑白边

题意:n个点,m条边,每条边连接两个点,边权值有0,有1,问若图不连通,输出-1,否则,输出最少用多少个1

这就很显然了,只需要用kruskal跑一遍就OK了,因为0边在前,1边在后

#include <bits/stdc++.h>
using namespace std;
const int N = 200005;
struct Node{
  int x, y, z;
  bool operator< (const Node &W) const{
    return z < W.z;
  }
}e[N];
int n, m;
int fa[N];
int get(int x)
{
  return fa[x] == x ? x : fa[x] = get(fa[x]);
}
void kruskal()
{
  int cnt = 0, ct = 0;
  for (int i = 0; i < m; i ++ )
  {
    int x = e[i].x, y = e[i].y, z = e[i].z;
    x = get(x), y = get(y);
    if (x == y) continue;
    fa[x] = y;
    cnt ++;
    if (z) ct ++;
  }
  if (cnt < n - 1) cout << "-1" << "\n";
  else cout << ct << "\n";
}
int main(void)
{
  scanf("%d%d", &n, &m);
  for (int i = 1; i <= n; i ++ ) fa[i] = i;
  for (int i = 0; i < m; i ++ )
  {
    int x, y, z; scanf("%d%d%d", &x, &y, &z);
    e[i] = {x, y, z};
  }
  sort(e, e + m);
  kruskal();
  return 0;
}

B 最好的宝石

单值修改、区间查询,只有pushup的线段树

#include <bits/stdc++.h>
using namespace std;
const int N = 200005;
int n, m;
int w[N];
struct Node{
  int l, r;
  int mx, cnt;
}tr[N * 4];
struct Seg_Tree{
  void pushup(Node &c, Node &a, Node &b)
  {
    if (a.mx == b.mx) c.cnt = a.cnt + b.cnt;
    else if (a.mx > b.mx) c.cnt = a.cnt;
    else c.cnt = b.cnt;
    c.mx = max(a.mx, b.mx);
  }
  void pushup(int u)
  {
    pushup(tr[u], tr[u << 1], tr[u << 1 | 1]);
  }
  void build(int u, int l, int r)
  {
    if (l == r)
    {
      tr[u] = {l, r, w[l], 1};
      return;
    }
    tr[u] = {l, r};
    int mid = l + r >> 1;
    build(u << 1, l, mid); build(u << 1 | 1, mid + 1, r);
    pushup(u);
  }
  void change(int u, int x, int c)
  {
    if  (tr[u].l == x && tr[u].r == x)
    {
      tr[u] = {x, x, c, 1};
      return;
    }
    int mid = tr[u].l + tr[u].r >> 1;
    if (x <= mid) change(u << 1, x, c);
    if (x > mid) change(u << 1 | 1, x, c);
    pushup(u);
  }
  Node query(int u, int l, int r)
  {
    if (tr[u].l >= l && tr[u].r <= r) return tr[u];
    int mid = tr[u].l + tr[u].r >> 1;
    if (r <= mid) return query(u << 1, l, r);
    else if (l > mid) return query(u << 1 | 1, l, r);
    else
    {
      Node left = query(u << 1, l, r);
      Node right = query(u << 1 | 1, l, r);
      Node ans;
      pushup(ans, left, right);
      return ans;
    }
  }
}t;
int main(void)
{
  cin >> n >> m;
  for (int i = 1; i <= n; i ++ ) scanf("%d", &w[i]);
  t.build(1, 1, n);
  char op[10];
  int l, r;
  while (m -- )
  {
    scanf("%s", op);
    scanf("%d%d", &l, &r);
    if (*op == 'C')
    {
      t.change(1, l, r);
    }
    else
    {
      Node x = t.query(1, l, r);
      printf("%d %d\n", x.mx, x.cnt);
    }
  }
  return 0;
}

C 滑板上楼梯

一看到楼梯,就想到dp的状态机模型,额,但是一看n是1e12就给放弃了,在思考一下,贪心模拟给过了

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n;
int main()
{
  cin >> n;
  cout << n / 4 * 2 + (n % 4 == 3 ? 1 : n % 4) << endl;
  return 0;
}

D GCD

我们贪心的考虑,如果这个子集包含了1~n中全部的素数呢(因为是任意问题,所以考虑最坏情况),很显然gcd(xi,xj) = 1,在加上一个1呢,还是gcd(xi,xj) = 1,但是我们要是往里面在加一个数,那么这个数一定是合数,这个合数加入了集合,一定会和某个素数是倍数关系,那么一定合法。
所以有如下代码:

#include <bits/stdc++.h>
using namespace std;
const int N = 100005;
int n;
int p[N], cnt;
bool st[N];
void get_primes()
{
  for (int i = 2; i <= n; i ++ )
  {
    if (!st[i]) p[cnt ++ ] = i;
    for (int j = 0; i * p[j] <= n; j ++ )
    {
      st[i * p[j]] = true;
      if (i % p[j] == 0) break;
    }
  }
}
int main()
{
  cin >> n;
  get_primes();
  cout << (cnt + 2 > n ? -1 : cnt + 2) << endl;
  return 0;
}

牛牛的加法

模拟,注意要去掉前导0

#include <bits/stdc++.h>
using namespace std;
string a, b, c;
int main()
{
  cin >> a >> b;
  reverse(a.begin(), a.end());
  reverse(b.begin(), b.end());
  for (int i = 0; i < a.size() || i < b.size(); i ++ )
  {
    int t = 0;
    if (i < a.size()) t = a[i] - '0';
    if (i < b.size()) t += b[i] - '0';
    c += t % 10 + '0';
  }
  reverse(c.begin(), c.end());
  while (c.front() == '0' && c.size() > 1) c.erase(0, 1);
  cout << c << endl;
  return 0;
}

F- 石子合并

贪心的考虑,因为没有合并的过程,每次都是要从相邻两堆中那走较轻的一堆,但所得的成绩是两堆之和,那么贪心的考虑每次加上max的一堆。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 200005;
ll a[N];
int main()
{
  int n; cin >> n;
  ll mx = 0;
  ll sum = 0;
  for (int i = 1; i <= n; i ++ )
  {
    scanf("%lld", &a[i]);
    mx = max(mx, a[i]);
    sum += a[i];
  }
  cout << sum + mx * (n - 2) << endl;
  return 0;
}

G-滑板比赛

双指针呗,没啥好说的

#include <bits/stdc++.h>
using namespace std;
const int N = 200005;
int n, m;
int a[N], b[N];
int main()
{
  cin >> n >> m;
  for (int i = 0; i < n; i ++ ) scanf("%d", &a[i]);
  for (int i = 0; i < m; i ++ ) scanf("%d", &b[i]);
  sort(a, a + n);
  sort(b, b + m);
  // for (int i = 0; i < n; i ++ ) printf("%d ", a[i]); puts("");
  // for (int i = 0; i < m; i ++ ) printf("%d ", b[i]); puts("");
  int cnt = 0;
  for (int j = 0, i = 0; j < m; j ++ )
  {
    while (a[i] <= b[j] && i < n) i ++;
    // printf("i = %d, a[i] = %d, j = %d, b[j] = %d\n", i, a[i], j, b[j]);
    if (a[i] > b[j])cnt ++;
    i ++;
  }
  cout << cnt << "\n";
  return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值