LINK

考虑最坏情况, n n n个节点彼此由相邻的权值为 p p p的边构成最小生成树

此时答案 a n s = ( n − 1 ) ∗ p ans=(n-1)*p ans=(n1)p

考虑如何减小答案。

设一段区间 [ l , r ] [l,r] [l,r]内的最小数字是 x x x且其余数字都是 x x x的倍数

那么证明 [ l , r − 1 ] [l,r-1] [l,r1]都可以向 r r r节点连权值为 x x x的边

如果 x < p x<p x<p,那么就可以减小答案。

考虑用优先队列,每次弹出当前最小的数字,设索引为 u u u

那么 u u u往左边一直扩展到 l l l,满足 [ l , u − 1 ] [l,u-1] [l,u1]的所有数字是 a u a_u au的倍数

u u u往右边一直扩展到 r r r,满足 [ u + 1 , r ] [u+1,r] [u+1,r]的所有数字是 a u a_u au的倍数

那么区间 [ l , r ] [l,r] [l,r]内的 r − l r-l rl条边都可以由权值为 a u a_u au的边替换

而且,我们可以把 [ l , r ] [l,r] [l,r]的边标记起来,之后扩展的时候就不需要扩展这些边了

至此,我们可以在 O ( n l o g ( n ) ) O(nlog(n)) O(nlog(n))的时间内得到解,瓶颈是优先队列的

#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5+19;
const int mod = 1e9+7;
int n,base,a[maxn],vis[maxn];//i->i+1的边是否被覆盖 
typedef pair<int,int>p;
priority_queue<p,vector<p>,greater<p> >q;
signed main()
{
	int t; cin >> t;
	while( t-- )
	{
		cin >> n >> base;
		long long ans = 1ll*( 1ll*n-1 )*base;
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&a[i] );
			q.push( p(a[i],i) );
		}
		while( !q.empty() )
		{
			int u = q.top().second, val = q.top().first; q.pop();
			if( val>=base )	break;
			int r = u;
			while( r+1<=n && a[r+1]%val==0 && vis[r]==0 )	vis[r] = 1,r++;
			int l = u;
			while( l-1>=1 && a[l-1]%val==0 && vis[l-1]==0 )	l--,vis[l] = 1;
			ans = ans-1ll*(r-l)*base+1ll*(r-l)*val;
		}
		while( !q.empty() )	q.pop();
		cout << ans << endl;
		for(int i=1;i<=n;i++)	vis[i] = 0;
	}
}