原题链接:https://codeforces.ml/contest/1513/problem/D
题意
有n个点,每个点都有自己的权值ai
两点之间的连边满足以下要求:
- g c d ( a i , a i + 1 . . . a j ) = m i n ( a i , a i + 1 . . . a j ) gcd(a_{i},a_{i+1}...a_{j})=min(a_{i},a_{i+1}...a_{j}) gcd(ai,ai+1...aj)=min(ai,ai+1...aj) i和j可以连一条权值为 m i n ( a i , a i + 1 . . . a j ) min(a_{i},a_{i+1}...a_{j}) min(ai,ai+1...aj)的边
- i和i+1之间有一条权值为p的边
分析
如果暴力直接遍历找出边然后做一遍kruskal,时间复杂度是 O ( n 2 l o g n ) O(n^2logn) O(n2logn)肯定不满足要求,但我们发现,这题的序列其实是连续的,也就是说它是一个只有一条线的链。
然后我们思考kruskal是怎么做的,每次找到最小的边,然后加入集合中。
这题最小的边其实就是最小的数字,因此我们对点排序,每次选择最优的往两边延伸,有有两个重要的剪枝条件
- g c d ( v a l , a [ i ] ) ! = v a l gcd(val,a[i])!=val gcd(val,a[i])!=val就可以退出了,因为题中gcd是连续的区间
- 当前pos和i在同一个集合里就可以退出了,这是非常重要的剪枝!!!因为如果在一个集合中说明前面的点已经被更优的答案覆盖过了,不理解可以手动模拟一下
Code
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cmath>
#include <bitset>
#include <map>
#include <set>
#include <stack>
#include <queue>
#include <unordered_map>
using namespace std;
#define fi first
#define se second
#define re register
typedef long long ll;
typedef pair<ll, ll> PII;
typedef unsigned long long ull;
const int N = 5e5 + 10, M = 1e6 + 5, INF = 0x3f3f3f3f;
const int MOD = 1e9+7;
struct node {
int x, pos;
bool operator < (const node &rhs) const {
return x < rhs.x;
}
}a[N];
int pre[N], fa[N];
int find(int x) {return x == fa[x] ? x : fa[x] = find(fa[x]);}
void solve() {
int T; cin >> T; while (T--) {
int n, p; cin >> n >> p;
for (int i = 1; i <= n; i++) fa[i] = i;
for (int i = 1; i <= n; i++) cin >> a[i].x, a[i].pos = i, pre[i] = a[i].x;
sort(a+1, a+n+1);
ll ans = 1ll*(n - 1) * p;
for (int i = 1; i <= n; i++) {
int val = a[i].x;
int pos = a[i].pos;
if (val >= p) continue;
for (int j = pos+1; j <= n; j++) {
if (__gcd(val, pre[j]) != val) break;
if (find(j) != find(pos)) {
ans -= p;
ans += val;
fa[find(j)] = find(pos);
} else {
break;
}
}
for (int j = pos-1; j >= 1; j--) {
if (__gcd(val, pre[j]) != val) break;
if (find(j) != find(pos)) {
ans -= p;
ans += val;
fa[find(j)] = find(pos);
} else {
break;
}
}
}
cout << ans << endl;
}
}
signed main() {
ios_base::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
#ifdef ACM_LOCAL
freopen("input", "r", stdin);
freopen("output", "w", stdout);
#endif
solve();
return 0;
}