CF-Divide by Zero 2021 and Codeforces Round #714 (Div. 2)-1513D. GCD and MST【MST/思维】

该博客探讨了一种结合数论和图论的算法问题,即在给定数组和特定条件(gcd等于最小值)下构建边,并寻找最小生成树。题目中提出了在满足gcd条件的元素之间建立边,并在所有相邻元素间建立边,要求找到最小生成树的总权重。解决方案是通过从小到大遍历数组,识别满足gcd条件的序列并进行边的替换,以降低总权重。代码实现中使用了优先队列和vis数组辅助处理,最终得出最小生成树的权重。
摘要由CSDN通过智能技术生成

题目链接
题意:给定一个数组 { a 1 , a 2 , . . . , a n } \{a_1,a_2,...,a_n\} {a1,a2,...,an},正整数 p p p和连边方式:
1.若存在 i , j ( 1 ≤ i , j ≤ n ) i,j(1\le i,j \le n) i,j(1i,jn) g c d ( a i , a i + 1 , a i + 2 , … , a j ) = m i n ( a i , a i + 1 , a i + 2 , … , a j ) gcd(a_i, a_{i+1}, a_{i+2}, \dots, a_{j}) = min(a_i, a_{i+1}, a_{i+2}, \dots, a_j) gcd(ai,ai+1,ai+2,,aj)=min(ai,ai+1,ai+2,,aj),在 i , j i,j i,j之间连一条权重为 m i n ( a i , a i + 1 , a i + 2 , … , a j ) min(a_i, a_{i+1}, a_{i+2}, \dots, a_j) min(ai,ai+1,ai+2,,aj)的边。
2.在所有 i , i + 1 i,i+1 i,i+1之间连边,权重为 p p p
求最小生成树的权重
思路:
如果一段序列满足 g c d ( a i , a i + 1 , a i + 2 , … , a j ) = m i n ( a i , a i + 1 , a i + 2 , … , a j ) gcd(a_i, a_{i+1}, a_{i+2}, \dots, a_{j}) = min(a_i, a_{i+1}, a_{i+2}, \dots, a_j) gcd(ai,ai+1,ai+2,,aj)=min(ai,ai+1,ai+2,,aj),则所有数都是最小值的倍数。那么我们只要按照数值大小从小到大遍历数组,对于当前值 a p o s a_{pos} apos,把它当成具有上述序列性质的序列中的最小值,往两边扩展,只要是 a p o s a_{pos} apos的倍数都和 a p o s a_{pos} apos连边,如果 a p o s < p a_{pos}<p apos<p则在最后的最小生成树里用这条边去换一条权重为 p p p的边,整个过程中用 v i s [ i ] vis[i] vis[i]来标记 [ i , i + 1 ] [i,i+1] [i,i+1]之间的边是否被替换。
AC代码:

#include <bits/stdc++.h>
#define rep(i, x, y) for (int i = x; i < y; i++)
#define per(i, x, y) for (int i = x; i >= y; i--)
#define ll long long
using namespace std;

inline int read() {
  int x = 0, f = 1;
  char ch = getchar();
  while (ch < '0' || ch > '9') {
    if (ch == '-') f = -1;
    ch = getchar();
  }
  while (ch >= '0' && ch <= '9') {
    x = x * 10 + ch - '0';
    ch = getchar();
  }
  return x * f;
}

const int maxn = 2e5 + 10;
/***************main****************/
ll T = 1;
ll m, n;
ll a[maxn], vis[maxn];
vector<pair<ll, ll>> ma;
int main() {
  T = read();
  while (T--) {
    n = read();
    m = read();
    ma.clear();
    rep(i, 0, n) {
      a[i] = read();
      ma.push_back({a[i], i});
      vis[i] = 0;
    }
    ll ans = m * (n - 1);
    sort(ma.begin(), ma.end());
    for (int i = 0; i < n; i++) {
      ll pos = ma[i].second;
      if (vis[pos]) continue;
      for (int j = pos + 1; j < n; j++) {
        if (vis[j - 1] || a[j] % a[pos] != 0) break;
        vis[j - 1] = 1;
        if (a[pos] < m) ans += a[pos] - m;
      }
      for (int j = pos - 1; j >= 0; j--) {
        if (vis[j] || a[j] % a[pos] != 0) break;
        vis[j] = 1;
        if (a[pos] < m) ans += a[pos] - m;
      }
    }
    cout << ans << endl;
  }
  return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值