CF1513 D. GCD and MST
https://codeforces.com/problemset/problem/1513/D
题意:
有n个数,每个数代表一个点,点i和点i+1之间都有一条权值为p的边,若区间[i,j]的最小值等于它们的gcd,i和j之间连一条区间最小值的边
求最小生成树
区间最小值=区间gcd 等价于 区间每个数都是最小值的k倍
从小到大枚举所有的数
设现在枚举的是点i,对应数字为x,以该位置为中心向左向右扩展,设扩展出的区间为[a,b],那么[a,b]内除了第i个点的每个点都会向i连一条权值为x的边
区间[a,b]现在可以认为合并起来了
扩展区间只能扩展到还没有与当前区间合并的区间,当扩展的过程中遇到一个之前已合并的区间时,合并一次之后停止扩展。因为之前已合并的区间相当于他们已经以最小代价连成一棵树,现在这个区间也连成了一棵树,只需要再连一条就可以。
注意边权如果大于p了就直接用p即可
最后把所有区间用p连起来
#include<bits/stdc++.h> using namespace std; #define N 200002 int a[N]; struct node { int w,id; }e[N]; bool con[N]; bool cmp(node p,node q) { return p.w<q.w; } int main() { int T,n,p; long long ans; scanf("%d",&T); while(T--) { scanf("%d%d",&n,&p); for(int i=1;i<=n;++i) { scanf("%d",&a[i]); e[i].w=a[i]; e[i].id=i; } sort(e+1,e+n+1,cmp); for(int i=1;i<=n;++i) con[i]=false; ans=0; for(int i=1;i<=n && e[i].w<p;++i) { for(int j=e[i].id+1;j<=n && !con[j-1];++j) { if(a[j]%e[i].w) break; ans+=e[i].w; con[j-1]=true; } for(int j=e[i].id-1;j && !con[j];--j) { if(a[j]%e[i].w) break; ans+=e[i].w; con[j]=true; } } for(int i=1;i<n;++i) if(!con[i]) ans+=p; printf("%lld\n",ans); } }