题解: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;
}
posted @ 2025-07-13 12:03  Cefgskol  阅读(5)  评论(0)    收藏  举报