题意:有n头牛,每头牛有两个属性,音量v,在X轴上的坐标x。 牛 i 牛_i 牛i和 牛 j 牛_j 牛j想要通话,音量至少是 m a x ( v i , v j ) ∗ a b s ( x i − x j ) max(v_i, v_j) * abs(x_i - x_j) max(vi,vj)∗abs(xi−xj),要求每对牛通话音量的和。
思路:
暴力做法就是
∑
i
=
0
n
∑
j
=
0
i
−
1
(
m
a
x
(
v
i
,
v
j
)
∗
a
b
s
(
x
i
−
x
j
)
)
\sum_{i=0}^n\sum_{j=0}^{i-1}(max(v_i, v_j) * abs(x_i - x_j))
∑i=0n∑j=0i−1(max(vi,vj)∗abs(xi−xj)),复杂度
O
(
n
2
)
O(n^2)
O(n2)。
我们按每头牛的
v
i
v_i
vi排序,可以把上面的式子化简为
∑
i
n
∑
j
i
−
1
(
v
i
∗
a
b
s
(
x
i
−
x
j
)
)
\sum_i^n\sum_j^{i-1}(v_i * abs(x_i - x_j))
∑in∑ji−1(vi∗abs(xi−xj))
我们知道第
i
i
i头牛有
i
−
1
i-1
i−1头牛比它的音量小,在把这
i
−
1
i-1
i−1头牛分在这头牛
x
i
x_i
xi的左边和右边,把绝对值
a
b
s
abs
abs给去掉,设左边牛的
∑
x
j
\sum x_j
∑xj为
s
l
_
d
i
s
sl\_dis
sl_dis,总头数为
l
_
n
u
m
l\_num
l_num;右边牛的
∑
x
j
\sum x_j
∑xj为
s
l
_
d
i
s
sl\_dis
sl_dis,总头数为
r
_
n
u
m
r\_num
r_num;
i
−
1
i-1
i−1头牛的总和为
s
_
d
i
s
s\_dis
s_dis,总头数为
n
u
m
=
i
−
1
num = i-1
num=i−1。
就把公式变成了
∑
i
=
1
n
v
i
∗
(
l
_
n
u
m
∗
x
i
−
s
l
_
d
i
s
+
s
r
_
d
i
s
−
r
_
n
u
m
∗
x
i
)
\sum_{i=1}^nv_i *(l\_num*x_i -sl\_dis+sr\_dis-r\_num*x_i)
∑i=1nvi∗(l_num∗xi−sl_dis+sr_dis−r_num∗xi)。
中间求
s
l
_
d
i
s
sl\_dis
sl_dis,
s
r
_
d
i
s
sr\_dis
sr_dis,
s
_
d
i
s
s\_dis
s_dis,
l
_
n
u
m
l\_num
l_num,
r
_
n
u
m
r\_num
r_num,
n
u
m
num
num。就要用到树状数组,添加和查询每次操作都是
l
o
g
(
n
)
log(n)
log(n)复杂度。所以总复杂度就是
O
(
n
l
o
g
(
n
)
)
O(nlog(n))
O(nlog(n))。
在这题中构建了两个树状数组,一个记录了牛的距离,一个记录了牛的头数。
因为是边查询边构树,保证了:在第
i
i
i头牛查询距离和头数时树状数组中都是
v
v
v小于
v
i
v_i
vi的牛的和。
其中数组下标表示牛在X轴上的坐标。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#include<algorithm>
using namespace std;
typedef long long ll;
const ll N = 2e4+10;
ll tree[2][N];//1距离,0个数。
//每头牛的音量和距离,可以按音量大小从小到大排序
struct cow {
ll v, x;
inline bool operator < (cow U) const {
return v < U.v;
}
} C[N];
//对树状数组添加元素,每次都会在坐标x_i添加坐标和头数。
void add(ll p, ll val) {
while(p <= N) {
tree[1][p] += val;
tree[0][p] += 1;
p += (p & (-p));
}
}
//找出在该坐标下(左边的坐标总和)和(左边牛的头数总和)
ll query(ll p, ll k) {
ll ans = 0;
while(p) {
ans += tree[k][p];
p -= (p & (-p));
}
return ans;
}
int main() {
// freopen("in.txt", "r", stdin);
// freopen("out.txt", "w", stdout);
ll n, l_num, num, r_num, sr_dis, sl_dis, s_dis, ans = 0;
scanf("%lld", &n);
for(int i=0; i<n; i++) {
scanf("%lld%lld", &C[i].v, &C[i].x);
}
sort(C, C+n);
for(int i=0; i<n; i++) {//从0开始,和上面有点偏差。
num = i;//小于v_i的牛的总头数
l_num = query(C[i].x, 0);//x_i左边的牛的头数
r_num = num - l_num;//x_i右边的牛的头数
s_dis = query(20000, 1);//牛的坐标总和
sl_dis = query(C[i].x, 1);//x_i左边牛的坐标总和
sr_dis = s_dis - sl_dis;//x_i右边牛的坐标总和
ans += (C[i].x * l_num - sl_dis + sr_dis - C[i].x * r_num) * C[i].v;
add(C[i].x, C[i].x);//添加元素,维护坐标和与牛的头数和。
}
printf("%lld", ans);
return 0;
}