题目:
给n个点,可以在任意两个点之间建立边,每条边的值都是1。允许添加不超过m条边,问 ∑ni=1 ∑nj=1 dist(i,j)的最小值是多少。若i,j无法连通,则规定dist(i,j)=n,否则等于最短路的长度。
分析:
这道题在比赛的时候没有做出来,能看出来是找规律推公式。当时直接试图找出一种令距离最短的构造边的方式,先围一个圈,然后再把所有相隔一个点的点连起来,再然后是相隔两个,以此类推。可发现这样计算不太会继续距离,便放弃了这个想法。队友提供了一种思路,先满足局部最优,即令m条边先满足k个点是最优状态,然后如果有剩的边再加进去,没有就直接算距离。可后来发现加进去剩余的边也出了问题,这样貌似不能构造出最优解。于是这道题至此彻底做不出。
其实比赛过程中离正确答案很近过。若现在边数大于等于最优状态时的边数,那么任意两个点之间的距离都是1。(下面分析的思路是重点)此时,如果在“完美”的基础上每少一条边,那么总距离就会增加 2 。直到m = n - 1 ,此时一个点在中间,和剩下点直接相连。在此之前,所有点都可通过中间这个点以最大距离不超过2的代价到达任意一点,所以每去一条边总距离加2。在此之后,每去一条边,就会有一个孤立的点。重新算距离即可。
应该反思自己的思考能力。要有逻辑性。
代码:
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#include <vector>
#include <set>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <cstdio>
using namespace std;
#define ms(a,b) memset(a,b,sizeof(a))
typedef long long ll;
const double EPS=1e-8;
const int INF=0x3f3f3f3f;
ll n,m;
int main(){
int T;
scanf("%d",&T);
while(T--){
scanf("%lld%lld",&n,&m);
ll ans = 0;
if(m >= n*(n-1)/2){
ans = n*(n-1);
}else if(m >= n-1){
ans = n*(n-1) + 2*(n*(n-1)/2-m);
}else{
ans = m+n*(n-m-1) + m*(1+2*(m-1)+n*(n-m-1)) + (n-m-1)*n*(n-1);
}
printf("%lld\n",ans);
}
return 0;
}