题目:
Time Limit: 1000MS | Memory Limit: 65536K | |
Total Submissions: 4515 | Accepted: 1190 |
Description
Input
Output
Sample Input
5 1 5 3 2 4
Sample Output
40
Hint
Source
这道题目意思是要求直线上N个点相互之间距离的总和。一般就是建立一个二重循环,复杂度为O(n^2),由于本题目n的最大值可以达到1000000000,因此这样做肯定超时。解答思路是先根据点的位置从小到大对点进行排序,并求出排好序后的点中第1个点到其它点的距离:s(0)。接着递推求出s(i), i=1,2,...N-1。假设已知s(i),通过s(i)与s(i+1)的递推关系,求出s(i+1)。d(i,i+1)表示第i个点与第i+1个点之间的距离。
s(i+1) = Sum(d(i+1,j), j != i+1)
= Sum(d(k1,i+1),0<=k1<i+1) + Sum(d(i+1,k2),i+1<k2<N)
= Sum(d(k1,i) + d(i,i+1),0<=k1<i+1) + Sum(d(i,k2) - d(i,i+1),i+1<k2<N)
= Sum(d(k1,i),0<=k1<i+1) + (i+1) * d(i,i+1) + Sum(d(i,k2),i+1<k2<N) - (N-i-2) * d(i,i+1)
= Sum(d(k1,i), 0<=k1<i) + Sum(d(i,k2),i+1<k2<N) + (i+1) * d(i,i+1) - (N-i-2) * d(i,i+1)
= Sum(d(k1,i), 0<=k1<i) + Sum(d(i,k2),i+1<=k2<N) + i * d(i,i+1) - (N-i-2) * d(i,i+1)
= s(i) + i * d(i,i+1) - (N-i-2) * d(i,i+1)
从这个递推式可以看出,s(i+1)只与s(i)相关,因此在实际实现中不需要开一个数组来记录每个s(i),而只需要记录上一次求出的s(i)。每次求出s(i),就累加到最后的总和中。
由于本题的数值特别大,总和的最大值可以达到10^17,每个s(i)可以达到10^13。第一次实现用double来表示这些数据,结果一直出错。后来用了一个高精度的模板进行了修改才通过。double不能通过的原因可能是跟有效数字位数有关,double的有效数字位数为15~16位,虽然double完全能够满足本题中的数值范围。高精度的程序来自网上一个模板。
源代码:
#include <iostream>
using namespace std;
const int Base=1000000000;
const int Capacity=100;
struct BigInt{
int Len;
int Data[Capacity];
BigInt():Len(0){}
BigInt(const BigInt &V):Len(V.Len){memcpy(Data,V.Data,Len*sizeof*Data);}
BigInt(int V):Len(0){for(;V>0;V/=Base) Data[Len++]=V%Base;}
BigInt(unsigned long V):Len(0){for(;V>0;V/=Base) Data[Len++]=V%Base;}
BigInt &operator=(const BigInt &V){Len=V.Len;memcpy(Data,V.Data,Len*sizeof*Data);return *this;}
int &operator[](int Index){return Data[Index];}
int operator[](int Index)const{return Data[Index];}
};
int compare(const BigInt &A,const BigInt &B){
if(A.Len!=B.Len) return A.Len>B.Len ? 1:-1;
int i;
for(i=A.Len-1;i>=0 && A[i]==B[i];i--);
if(i<0)return 0;
return A[i]>B[i] ? 1:-1;
}
BigInt operator+(const BigInt &A,const BigInt &B){
int i,Carry(0);
BigInt R;
for(i=0;i<A.Len||i<B.Len||Carry>0;i++){
if(i<A.Len) Carry+=A[i];
if(i<B.Len) Carry+=B[i];;
R[i]=Carry%Base;
Carry/=Base;
}
R.Len=i;
return R;
}
BigInt operator-(const BigInt &A,const BigInt &B){
int i,Carry(0);
BigInt R;
R.Len=A.Len;
for(i=0;i<R.Len;i++){
R[i]=A[i]-Carry;
if(i<B.Len) R[i]-=B[i];
if(R[i]<0) Carry=1,R[i]+=Base;
else Carry=0;
}
while(R.Len>0&&R[R.Len-1]==0) R.Len--;
return R;
}
BigInt operator*(const BigInt &A,const int &B){
int i;
unsigned long int Carry = 0;
BigInt R;
for(i=0;i<A.Len||Carry>0;i++){
if(i<A.Len) Carry+=A[i]*B;
R[i]=Carry%Base;
Carry/=Base;
}
R.Len=i;
return R;
}
istream &operator>>(istream &In,BigInt &V){
char Ch;
for(V=0;In>>Ch;){
V=V*10+(Ch-'0');
if(In.peek()<=' ') break;
}
return In;
}
ostream &operator<<(ostream &Out,const BigInt &V){
int i;
Out<<(V.Len==0 ? 0:V[V.Len-1]);
for(i=V.Len-2;i>=0;i--) for(int j=Base/10;j>0;j/=10) Out<<V[i]/j%10;
return Out;
}
static int f(const void *a, const void *b)
{
unsigned long r1 = (*((unsigned long *)a));
unsigned long r2 = (*((unsigned long *)b));
if(r1 == r2)
return 0;
if(r1 > r2)
return 1;
return -1;
}
int main(int argc,char **argv)
{
unsigned long T[10000];
int N;
cin>>N;
BigInt cur;
BigInt sum;
int i;
for(i=0;i<N;i++)
cin>>T[i];
qsort(T,N,sizeof(T[0]),f);
for(i=1;i<N;i++)
{
unsigned long val = T[i] - T[0];
BigInt valInt(val);
cur = (cur + valInt);
}
sum = (sum + cur);
for(i=0;i<N-1;i++)
{
int A = i;
int B = N-i-2;
BigInt v(T[i+1] - T[i]);
BigInt w1 = v*A;
BigInt w2 = v*B;
cur = (cur + w1);
cur = (cur - w2);
sum = (sum + cur);
}
cout<<sum<<endl;
/*
unsigned long T[10000];
int N;
double cur = 0;
long double sum = 0;
char buf[1000];
int i,j;
cin>>N;
for(i=0;i<N;i++)
cin>>T[i];
qsort(T,N,sizeof(T[0]),f);
for(i=1;i<N;i++)
{
cur = cur + T[i] - T[0];
}
sum = sum + cur;
for(i=0;i<N-1;i++)
{
int A = i;
int B = N - i - 2;
double d = T[i+1] - T[i];
cur = cur + A * d - B * d;
sum = sum + cur;
}
sprintf(buf,"%f",sum);
j = 0;
while(buf[j] && buf[j] != '.')
cout<<buf[j++];
cout<<endl;
*/
return 0;
}
main函数中就是第一次用double来表示写的程序
高精度程序来自于网络。