题目就是求$egin{align}sum_{i=1}^nsum_{j=i+1}^nsum_{k=j+1}^nsum_{l=k+1}^n&|(a_i+a_j)-(a_k+a_l)|+|(a_i+a_k)-(a_j+a_l)|\+&|(a_i+a_l)-(a_j+a_k)|+|(a_j+a_k)-(a_i+a_l)|\+&|(a_j+a_l)-(a_i+a_k)|+|(a_k+a_l)-(a_i+a_j)|end{align}$
朴素的O(n4)显然过不了,我们得考虑一下优化下。
观察公式我们发现它是两两配对进行相减,我们可以尝试将a[i]+a[j]两两配对储存在数组b里,然后计算每对对答案的贡献。
绝对值不好处理, 我们可以尝试去掉绝对值,那么我们就得确定该结果的正负。我们将b数组从大到小排序,发现对于一个bi,它会被减去(i-1)次,加上(nn-i)次(其中nn为b数组里数的个数)
但是我们会发现有个问题,就是有可能会选到具有同一个数的bi,bj,比如bi=ax+ay,但bj=ax+az,这是不合法的情况,因为三个人不符题目要求,我们就要将这一些去掉。
对于一个bi,它是由ax+ay得到的,其中ax>ay,我们考虑bj=ax+az,其中假设bi>bj,那么ay>az,对于这些的az+ax=bj都是不合法的,那么一共就有rank[ay]-1个。(rank[i]表示i的排名)
再对于bk=as+ay,假设bi>bk,那么ax>as,对于这些就有rank[ax]-2个(除去它本身和ay)
bi<bj bi<bk同样的道理也可以得到(n-rank[ay]-1)个和(n-rank[ax])个
这样我们就可以把重复的去掉就可以了。
复杂度O(n2+nlogn2)
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #define N 2002000 6 #define mo 1000000007 7 using namespace std; 8 int a[2002],t; 9 long long ans,n,len; 10 struct data{ 11 long long x,y,v; 12 }b[N]; 13 bool comp(const struct data a,const struct data b){ 14 return a.v>b.v; 15 } 16 int main(){ 17 scanf("%d",&t); 18 while (t--){ 19 scanf("%lld",&n); 20 ans=0; 21 for (int i=1;i<=n;i++) 22 scanf("%d",&a[i]); 23 sort(a+1,a+1+n); 24 len=0; 25 for (int i=1;i<=n;i++) 26 for (int j=i+1;j<=n;j++) 27 b[++len].v=a[j]+a[i],b[len].x=i,b[len].y=j; 28 sort(b+1,b+1+len,comp); 29 for (long long i=1;i<=len;i++) 30 ans=(b[i].v%mo*(len-i-(b[i].x-1+b[i].y-2))%mo-b[i].v%mo*(i-1-(n-b[i].x-1+n-b[i].y))%mo+ans+mo)%mo; 31 printf("%lld ",ans*2%mo); 32 } 33 }
这道题就是通过拆散式子把相等的式子一起加起来从而降低了复杂度。