CF1656F Parametric MST 题解
为了便于解题,先对
首先,根据定义可以得出总价值的表达式:
接着,我们需要发现一个比较重要的性质:
-
w_{i,j}(t)=a_ia_j+t(a_i+a_j)=(a_i+t)(a_j+t)-t^2
也就是说,如果固定一个
现在来考虑一下无解的情况:
-
如果
\forall 1\le i\le n,a_i+t>0 ,那么除了1 以外的所有结点向1 连边,有\begin{aligned} W&=\sum\limits_{(u,v)\in E}a_ua_v+t\sum\limits_{(u,v)\in E}(a_u+a_v)\\ &=a_1\sum\limits_{i=2}^na_i+t\left[(n-1)a_1+\sum\limits_{i=2}^na_i\right]\\ \end{aligned} 可以发现,
W 是关于t 的一次函数。由于满足\forall 1\le i\le n,a_i+t>0 ,所以如果一次项系数(n-1)a_1+\sum\limits_{i=2}^na_i>0 ,那么当t\rightarrow+\infty 时W\rightarrow+\infty ,该函数不存在最大值。 -
如果
\forall 1\le i\le n,a_i+t<0 ,同理有:\begin{aligned} W&=a_n\sum\limits_{i=1}^{n-1}a_i+t\left[(n-1)a_n+\sum\limits_{i=1}^{n-1}a_i\right]\\ \end{aligned} 类似的,如果
(n-1)a_n+\sum\limits_{i=1}^{n-1}a_i<0 ,当t\rightarrow-\infty 时,W\rightarrow+\infty ,不存在最大值。
综上,如果 INF 即可。
现在来考虑有解时如何寻找解。这时我们就需要引入一个新的性质:
-
如果
W 能取到最大值,-t 的值一定是a 数组中的其中一个元素的值。证明:
容易得知当
W 取到最值时,a_1\le -t\le a_n (否则函数不收敛)。当
-t\in[a_i,a_{i+1}](1\le i<n) 时,最优的连边方式之一是将所有的1\le j\le i 向结点n 连边(因为这些j 满足a_j+t\le 0 ),其他结点向1 连边。所以我们可以得到W 的表达式:\begin{aligned} W&=a_n\sum\limits_{j=2}^ia_j+t\left[(i-1)a_n+\sum\limits_{j=2}^ia_j\right]+a_1\sum\limits_{j=i+1}^na_j+t\left[(n-i)a_1+\sum_{j=i+1}^na_j\right] \end{aligned} (注意到这里
1 和n 之间的边仅仅被连了一次,所以最终不用考虑重边的影响。)在这里因为
i 确定,所以W 还是关于t 的一次函数,最值必然在-t=a_i 或-t=a_{i+1} 时取到。命题得证。
接下来只用枚举
中间那一些求和的式子可以使用前缀和维护。
放代码:
#include<bits/stdc++.h>
#define int long long
using namespace std;
main(){
ios::sync_with_stdio(false);
int t; cin>>t;
while(t--){
int n,c=LLONG_MIN; cin>>n;
vector<int> a(n),s;
for(auto &i:a)cin>>i;
sort(a.begin(),a.end()); // 排序
partial_sum(a.begin(),a.end(),back_inserter(s)); // 做前缀和
if(a[0]*(n-2)+s[n-1]>0||a[n-1]*(n-2)+s[n-1]<0)cout<<"INF\n"; // 无解情况
else{
for(int i=0;i<n;i++)
c=max(c,a[0]*(s[n-1]-s[i])-a[i]*(a[0]*(n-i-1)+s[n-1]-s[i])+a[n-1]*(s[i]-s[0])-a[i]*(a[n-1]*i+s[i]-s[0]));
// 带入表达式计算
cout<<c<<endl;
}
}
return 0;
}