Divide by Zero 2021 and Codeforces Round #714 (Div. 2) D. GCD and MST (最小生成树)
-
题意:有一组数\(a\),如果\(gcd(a_i,a_{i+1},...,a{j})=min(a_i,...,a{i+1},...,a{j})\),那么\(i\)和\(j\)之间就可以连一条边,边权为\(min(a_i,...,a{i+1},...,a{j})\).\(i\)和\(i+1\)之间都可以连一天边权为\(p\)的边,现在给你这\(n\)个点,求最小生成树.
-
题解:根据kruskal的贪心思想,我们要求最小生成树,一定是先连边权小的边,所以我们可以对\(a\)先排序,并记录每个\(a_i\)在原数组中的位置,枚举排序后的\(a\),以当前的数同时作为\(gcd\)和\(min\),在原数组对应的位置分别向两边拓展连边,并对能连的点标记.
-
代码:
#include <bits/stdc++.h> #define ll long long #define fi first #define se second #define pb push_back #define me memset #define rep(a,b,c) for(int a=b;a<=c;++a) #define per(a,b,c) for(int a=b;a>=c;--a) const int N = 1e6 + 10; const int mod = 1e9 + 7; const int INF = 0x3f3f3f3f; using namespace std; typedef pair<int,int> PII; typedef pair<ll,ll> PLL; ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;} ll lcm(ll a,ll b) {return a/gcd(a,b)*b;} int main(){ ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); int _; cin>>_; while(_--){ int n,x; cin>>n>>x; vector<int> a(n+1); vector<PII> v(n+1); vector<bool> st(n+1); rep(i,1,n){ cin>>a[i]; v[i]=(make_pair(a[i],i)); } sort(v.begin(),v.end()); ll ans=0; rep(i,1,n){ int cur=v[i].fi; //cur表示最小因子,去找能匹配的点 int pos=v[i].se; if(cur>=x) break; while(pos>1){ if(a[pos-1]%cur==0 && !st[pos-1]){ st[pos-1]=true; ans+=cur; pos--; } else break; } pos=v[i].se; while(pos<n){ if(a[pos+1]%cur==0 && !st[pos]){ st[pos]=true; ans+=cur; pos++; } else break; } } rep(i,1,n-1){ if(!st[i]) ans+=x; } cout<<ans<<'\n'; } return 0; }
𝓐𝓬𝓱𝓲𝓮𝓿𝓮𝓶𝓮𝓷𝓽 𝓹𝓻𝓸𝓿𝓲𝓭𝓮𝓼 𝓽𝓱𝓮 𝓸𝓷𝓵𝔂 𝓻𝓮𝓪𝓵
𝓹𝓵𝓮𝓪𝓼𝓾𝓻𝓮 𝓲𝓷 𝓵𝓲𝓯𝓮