题解:LGP5094 [USACO04OPEN] MooFest G 加强版
做法
考虑拆一下式子,原来是 \(\max\{v_i,v_j\}\times |x_i-x_j|\),首先,这种两个求 \(\max\) 的有一个经典套路,就是排序。我们先对 \(v,x\) 两个数组进行排序,以 \(v\) 的大小为第一关键字。容易发现,排完序后,对于第 \(i\) 个位置,前 \(i-1\) 个位置和它去取 \(max\) 得到的结果都是 \(v_i\),我们把 \(v_i\) 提出来,那么第 \(i\) 个位置对于答案的贡献就是
\[v_i \times \sum_{j=1}^{i-1}|x_i-x_j|
\]
前面一部分的求法明显是 \(O(1)\) 的,重点关注后面这一部分,由于涉及到取绝对值,我们拆成两部分来求:
- 比 \(x_i\) 大。
- 比 \(x_i\) 小。
对于那也就是说,位置 \(i\) 的贡献变为
\[v_i\times (\sum{比 x_i 大的数的和}-比 x_i 大的数的数量 \times x_i+比x_i 小的数的数量 \times x_i \sum{比 x_i 小的数})
\]
这时候你就会发现可以直接套树状数组求了,当然,普通树状数组不行,需要权值树状数组,求就完事了。时间复杂度 \(O(n)\)。
CODE
#include<bits/stdc++.h>
#define int long long
using namespace std;
int c[50005],ct[50005],n;
struct C{
int v,x;
}a[50005];
int lb(int x){return x&(-x);}
void upd(int k,int x,int c[]){
for(int i=k;i<=50000;i+=lb(i))c[i]+=x;
}
int qry(int k,int c[]){
int r=0;
for(int i=k;i>=1;i-=lb(i))r+=c[i];
return r;
}
bool cmp(C x,C y){return x.v<y.v;}
signed main(){
cin>>n;
for(int i=1;i<=n;++i)cin>>a[i].v>>a[i].x;
sort(a+1,a+n+1,cmp);
long long ans=0;
for(int i=1;i<=n;++i){
int x=a[i].x;
int cl=qry(x-1,ct);
int sl=qry(x-1,c);
int cm=qry(50000,ct)-qry(x,ct);
int sm=qry(50000,c)-qry(x,c);
ans+=1LL*a[i].v*(1LL*x*cl-sl);
ans+=1LL*a[i].v*(sm-1LL*x*cm);
upd(x,1,ct);
upd(x,x,c);
}
cout<<ans;
return 0;
}