2020 年百度之星·程序设计大赛 - 初赛二 Distance(组合数学)

Distance

题目链接:http://bestcoder.hdu.edu.cn/contests/contest_showproblem.php?cid=890&pid=1002

Problem Description

小沃沃所在的世界是一个二维平面。他有 n 个朋友,第 i 个朋友距离他的距离为 a[i],小沃沃并不知道这些朋友具体在什么点上。
请问在最优情况下,小沃沃的朋友两两之间的欧几里得距离的和的最小值是几?
假设小沃沃的位置为 P 0 P_0 P0 = ( x 0 x_0 x0, y 0 y_0 y0),第 i 个朋友的位置为 P i P_i Pi= ( x i x_i xi, y i y_i yi),对于所有的 i,需要满足 dist( P 0 P_0 P0, P i P_i Pi) = a[ i ],并且 ∑ i = 1 n − 1 ∑ _{i=1} ^{n−1} i=1n1 ∑ j = i + 1 n ∑ _{j=i+1}^{n} j=i+1n dist( P i Pi Pi, P j Pj Pj) 最小,其中 dist(X,Y)dist(X,Y) 为连接点 X 和点 Y 的线段的长度。 x i x_i xi, y i y_i yi都可以是任意实数。

Input

第一行一个正整数 test(1≤test≤10) 表示数据组数。
对于每组数据,第一行一个正整数 n(1≤n≤100000)。
接下来一行 n 个整数,第 i 个整数 a[ i ] (1≤a[ i ]≤1000000000) 表示第 i 个朋友和小沃沃的距离。

Ouput

对每组数据输出一行一个数,表示 ∑ i = 1 n − 1 ∑ _{i=1} ^{n−1} i=1n1 ∑ j = i + 1 n ∑ _{j=i+1}^{n} j=i+1n dist( P i Pi Pi, P j Pj Pj)的最小值。答案需要四舍五入到整数。

Sample Input

2
2
3 5
5
1 2 3 4 5

Sample Output

2
20


Solution

首先我们很容易想到,以小沃沃为左端点形成的射线距离是最短的。我们可以假设小沃沃的位置在0点处,如果第 i 个朋友和小沃沃的距离为a [ i ],那么我们就可以认为第 i 个朋友在a [ i ] 处。


有了以上的思路,我们就很容易根据输入建立一条直线模型,小沃沃在0处,第 i 个朋友在a [ i ] 处。


那么最简单的想法就是,我们枚举直线任意两点的距离,累加求和即可。但是观察数据范围我们发现 1≤n≤100000 ,因此 O( n 2 n^2 n2) 时间复杂度的暴力枚举算法会直接得到TLE的结果。


如果我们仔细地想想,会发现相邻点形成的线段,会被重复计算多次。那么我们可不可以先统计一下每条线段(相邻点形成的线段)共被计算了多少次呢?然后再将线段长度乘以次数,并累加求和。我们可以看下面这张图,假设n=5。
在这里插入图片描述

  • 当我们计算点1与其他所有点的距离的时候,会发现线段a被计算了4次。
  • 那么线段b又被计算了多少次呢?首先我们将所有点进行分组,线段b左边的为一组(点1和点2),线段b右边的为另外一组(点3、点4和点5)。我们可以发现当计算线段b左边的点线段b右边的点的距离时,线段b将会被计算进去,因此线段b被计算的次数是2*3=6次。即线段b左边点的数目乘以线段b右边点的数目。
  • 线段c的计算次数以此类推。线段c左边的点为(点1、点2和点3),线段c右边的点为(点4和点5),线段c的计算次数=3*2=6。
  • 那么我们尝试计算第i个点与第i+1个点形成的线段的计算次数。该线段左边有i个点,右边有n-i个点,因此计算次数为 i x (n-i)

解题思路基本形成,n个点一共会形成n-1个线段(此线段是指相邻顶点形成的线段),那么我们只需要对这n-1条线段分别计算累加次数,最后用线段长度乘以累加次数,并对每条线段进行累加即可。

Code

#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<stack>
#include<vector>
#include<cstdio>
#include<bitset>
#include<cstring>
#include<iostream>
#include<algorithm>

#define X first
#define Y second
#define ac cout<<ans<<endl
#define lowbit(x) ((-x)&(x))
#define mst(a,b) memset(a,sizeof(a),b)
#define debug(x) cout<<"#x"<<":"<<x<<endl
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie


using namespace std;
typedef long long ll;
typedef pair<int,int>pi;

const int maxn=1e5+100;
const int N=1e5+100;
const int M=1e5+100;
const int mod=1e9+7;

int m,T,p;
int n;
ll a[N];
ll dis[N];

template<class T> void qr(T &x)
{
    int f=0;
    x=0;
    char c=getchar();
    for(; !isdigit(c); c=getchar())
        f^=c=='-';
    for(; isdigit(c); c=getchar())
        x=(x<<3)+(x<<1)+(c^48);
    x=f?(-x):x;
}

template<class T> void qw(T x)
{
    if(x>=10)
        qw(x/10);
    putchar(x%10+'0');
}

int main()
{
#ifdef LOCAL
    freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
#endif
    qr(T);
    while(T--)
    {
        ll ans=0;
        qr(n);
        for(int i=1; i<=n; i++)
            qr(a[i]);
        sort(a+1,a+1+n); //从小到大排序

        for(int i=1; i<n; i++)
            dis[i]=a[i+1]-a[i];  //求相邻两点的距离

        for(int i=1; i<n; i++)
            ans+=dis[i]*(1LL*i)*(1LL*(n-i)); //计算结果  公式:ans= dis[i]*i*(n-i)

        printf("%lld\n",ans);
    }
}

©️2020 CSDN 皮肤主题: 1024 设计师:上身试试 返回首页