题目描述
MooFest
解法:多重树状数组(C++)
我们先来翻译题目,每头牛有两个值 vvv 和 xxx,两头牛对话所要的能量是 max(vi,vj)?abs(xi?xj)max(v_i, v_j)*abs(x_i-x_j)max(vi?,vj?)?abs(xi??xj?)
要求的是所有牛对话所需要的能量,即
∑in∑j≠imax(vi,vj)?abs(xi?xj)\sum_i^n\sum_{j\neq i}max(v_i, v_j)*abs(x_i-x_j)i∑n?j??=i∑?max(vi?,vj?)?abs(xi??xj?)
现在明白题意了吧
接着我们说做法,
先按照 vvv 从小到大排列一下,这样上面的式子就可以简化为
∑i>jvi?abs(xi?xj)=vi?∑i>jabs(xi?xj)\sum_{i>j}v_i*abs(x_i-x_j) = v_i*\sum_{i>j}abs(x_i-x_j)i>j∑?vi??abs(xi??xj?)=vi??i>j∑?abs(xi??xj?)
那么,接下来的事情就是求距离和,我们将距离划分为两部分:xxx 小于 xix_ixi?的部分和 xxx 大于 xix_ixi?的部分,放到数轴上连看就是在 iii 左边的和在 iii 右边的。
对于左边的距离和,我们有 xi?cnt?disx_i*cnt-disxi??cnt?dis,其中 cntcntcnt 表示左边部分的牛的个数,disdisdis 表示左边部分牛的 xxx 的和。
为什么这个公式有效呢?
接着我们看右边部分,all?disall -disall?dis 剩下的是就是右边部分的牛的 xxx 的和,右边部分的牛的个数等于i?cnt?1i-cnt-1i?cnt?1,于是,最终的计算公式即 all?dis?(i?cnt?1)?xiall - dis - (i-cnt-1)*x_iall?dis?(i?cnt?1)?xi?
通过上面分析,看得出来,我们需要用到两个树状数组,一个用于记录 xxx,一个用于记录对应 xxx 的个数
#include <iostream>
#include <algorithm>using namespace std;typedef long long ll;const ll N = 1e5;
ll c[2][N];
ll n, cnt, dis, lleft, rright, all=0, ans=0;struct Cow{
int v, x;
}cows[N];bool cmp(const Cow& a, const Cow& b){
return a.v<b.v;}ll lowbit(ll x){
return x&(-x);}void add(ll x, int op){
for(ll i=x;i<=N;i+=lowbit(i))c[op][i] += op?x:1;
}ll sum(ll x, int op){
ll s = 0;for(ll i=x;i;i-=lowbit(i))s += c[op][i];return s;
}int main()
{
cin >> n;for(int i=1;i<=n;i++)cin >> cows[i].v >> cows[i].x;sort(cows+1, cows+1+n, cmp);for(int i=1;i<=n;i++){
cnt = sum(cows[i].x, 0);dis = sum(cows[i].x, 1);lleft = cnt*cows[i].x-dis;rright = all-dis-(i-cnt-1)*cows[i].x;cout << cows[i].v << " " << cnt << " " << dis << " " << lleft << " " << right << endl;ans += (lleft+rright)*cows[i].v;all += cows[i].x;add(cows[i].x, 0);add(cows[i].x, 1);}cout << ans << endl;return 0;
}