JZOJ.5288【NOIP2017模拟8.17】球场大佬

时间:2022-12-17 21:11:05

Description

      每天下午,古猴都会去打羽毛球。但是古猴实在是太强了,他必须要到一些比较强的场去打。但是每个羽毛球场都有许多的人排着队,每次都只能上四个人,每个人都有自己的能力值,然而这四个人的总能力的高低与否才是古猴是否决定参加这个场的关键。
      每四个人的总能力值的定义是:任意选两个与另两个PK,能力值的贡献是较高的一组减去较低的一组。比如能力值为5和7的去PK 6和10的差值,那么用较高的减去较低的就是6+10-5-7=4。然后四个人的总能力值要任意两两之间与其他两个的总贡献。如(a,b,c,d)四个人,那么他们的总能力值就是(a,b)一组与(c,d)一组PK,(a,c)一组与(b,d)一组PK,(a,d)一组与(b,c)一组PK,(b,c)一组与(a,d)一组PK,(b,d)一组与(a,c)一组PK,(c,d)一组与(a,b)一组PK这六项PK差值就是四个人的总能力值。
     现在,古猴想知道这个场任意四个人的总能力值的和是多少,但是急着要拿拍,你需要马上告诉他这个场的情况?
 

Input

第一行一个T,接下来T组数据,每组数据第一行一个n,第二行n个整数a[i]表示每个人的能力值,a[i]∈[0,10^9]。

Output

输出一个整数表示任意四个人的总能力值。最后答案对10^9+7取模。
 

Sample Input

3
4
1 2 3 3
5
1 2 3 4 5
6
9 18 28 23 12 9

Sample Output

10
76
1176
 

Data Constraint

对于30%的数据n≤50,T≤5
对于另外20%的数据n≤200,T≤10
对于另外50%的数据n≤2000,T≤100
保证所有的n加起来不超过2000

题目就是求$\begin{align}\sum_{i=1}^n\sum_{j=i+1}^n\sum_{k=j+1}^n\sum_{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)

JZOJ.5288【NOIP2017模拟8.17】球场大佬JZOJ.5288【NOIP2017模拟8.17】球场大佬
 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\n",ans*2%mo);
32 }
33 }
神奇的代码

这道题就是通过拆散式子把相等的式子一起加起来从而降低了复杂度。