CF1656F Parametric MST 题解

· · 题解

为了便于解题,先对 a 数组从小到大进行排序。

首先,根据定义可以得出总价值的表达式:

\begin{aligned} W&=\sum\limits_{(u,v)\in E}[a_ua_v+t(a_u+a_v)]\\ &=\sum\limits_{(u,v)\in E}a_ua_v+t\sum\limits_{(u,v)\in E}(a_u+a_v) \end{aligned}

接着,我们需要发现一个比较重要的性质:

也就是说,如果固定一个 t,那么 t^2 就是定值,可以暂不考虑;\forall 1\le i\le n,如果 a_i+t 是正数,就向结点 1 连边以最小化该点的贡献;如果 a_i+t 是负数,就向结点 n 连边(如果 a_i+t=0 那么向哪个点连边都一样)。最后去掉 1n 之间的重边以及若干自环,即可构造出正好有 n-1 条边的最小生成树。

现在来考虑一下无解的情况:

综上,如果 (n-1)a_1+\sum\limits_{i=2}^na_i>0(n-1)a_n+\sum\limits_{i=1}^{n-1}a_i<0,边权和不存在最大值,直接输出 INF 即可。

现在来考虑有解时如何寻找解。这时我们就需要引入一个新的性质:

接下来只用枚举 i=1,2,\ldots,n,然后令 t=-a_i,将 t 带入证明中的那个表达式算出 W 的值。在所有的 W 中找到 W_{\max} 并输出即可。

中间那一些求和的式子可以使用前缀和维护。

放代码:

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