给定一个数组a,包含n (n > 2)个正整数和一个整数p。考虑一个无向加权图,有n个顶点,从1到n编号,其中顶点i和j (i < j)之间的边以以下方式添加:如果gcd(ai, ai+1, ai+2,, aj) = min(ai, ai+1, ai+2,, aj),则存在一条权值为min(ai, ai+1, ai+2,, aj)的边etwn和如果i + 1 = j,那么在i和j之间有一条权值为p的边。这里gcd(x, y,…)表示整数z, y, ....的最大公约数(gcd)请注意,如果上述两个条件都成立,i和j之间可能有多条边,如果i和j都不成立,那么这些顶点之间就没有边。目标是求出这个图的最小生成树的权值。输入第一行包含一个整数t (1 <t < 104)——测试用例的数量。每个测试用例的第一行包含两个整数n (2 <n<2-105)和p (1 < p < 10°)-节点的数量和参数p。第二行包含n个整数a1, a2, a3, ...., an (1 < ai < 10°)。它保证所有测试用例的n和不超过2 - 105。输出输出行。对于每个测试用例,打印对应图的权重。
Example
input
Copy
4 2 5 10 10 2 5 3 3 4 5 5 2 4 9 8 8 5 3 3 6 10 100 9 15
output
Copy
5 3 12 46
题解:
自己想着想着题意就想偏了,哎
按题意所说
其中顶点i和j (i < j)之间的边以以下方式添加:如果gcd(ai, ai+1, ai+2,, aj) = min(ai, ai+1, ai+2,, aj),则存在一条权值为min(ai, ai+1, ai+2,, aj)的边
假设区间最小值坐标为i,同时这个点也是gcd
这个区间在i两边的点是可以任意连边的,权值都是ai,既然如此我们可以让两边的点都连i,方便我们连边
从左到右记录左边最小gcd的位置,
从右往左也遍历一次,记录右边
#include<iostream>
#include<string>
#include<vector>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cmath>
#include<map>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
ll a[200050];
int l[200050];
int r[200050];
int f[200050];
struct node
{
ll l,r,w;
friend bool operator<(const node a,const node b)
{
return a.w < b.w;
}
};
int find(int x)
{
if(f[x] == x)
return f[x];
return f[x] = find(f[x]);
}
void solve()
{
ll n,k;
scanf("%lld%lld",&n,&k);
vector<node> p;
for(int i = 1;i <= n;i++)
{
scanf("%lld",&a[i]);
f[i] = i;
if(i != 1)
{
p.push_back({i,i-1,k});
}
}
l[1] = 1,r[n] = n;
for(int i = 2;i <= n;i++)
{
if(a[l[i-1]] <= a[i] && a[i]%a[l[i-1]] == 0)
{
l[i] = l[i-1];
}
else
{
l[i] = i;
}
}
for(int i = n - 1;i >= 1;i--)
{
if(a[r[i + 1]] <= a[i] && a[i]%a[r[i + 1]] == 0)
{
r[i] = r[i + 1];
}
else
{
r[i] = i;
}
}
for(int i = 1;i <= n;i++)
{
if(l[i] < i)
p.push_back({l[i],i,a[l[i]]});
if(r[i] > i)
p.push_back({r[i],i,a[r[i]]});
}
sort(p.begin(),p.end());
int cnt = 0;
ll ans = 0;
for(node t:p)
{
int x = find(t.l);
int y = find(t.r);
if(x != y)
{
cnt++;
ans += t.w;
f[x] = y;
}
if(cnt == n - 1)
break;
}
printf("%lld\n",ans);
}
signed main()
{
// ios::sync_with_stdio(0);
// cin.tie(0);cout.tie(0);
int t = 1;
scanf("%d",&t);
while (t--)
{
solve();
}
return 0;
}