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);
    }
}

 

posted @ 2021-09-13 17:08  TRTTG  阅读(53)  评论(0编辑  收藏  举报