POJ1990-MooFest(树状数组)

MooFest

题意:有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(xixj),要求每对牛通话音量的和。

思路:
暴力做法就是 ∑ 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=0nj=0i1(max(vi,vj)abs(xixj)),复杂度 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)) inji1(viabs(xixj))

我们知道第 i i i头牛有 i − 1 i-1 i1头牛比它的音量小,在把这 i − 1 i-1 i1头牛分在这头牛 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 i1头牛的总和为 s _ d i s s\_dis s_dis,总头数为 n u m = i − 1 num = i-1 num=i1
就把公式变成了 ∑ 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_numxisl_dis+sr_disr_numxi)
中间求 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;
}

猜你喜欢

转载自blog.csdn.net/weixin_45363113/article/details/107201581