mayoko’s diary

プロコンとかいろいろ。

POJ 1990 MooFest

解法

まず考え方ですが, v(i) が小さい順に牛を並べていく, というふうに考えた場合, i 番目の牛の volume threshold が v(i) 以下の牛と対をなしてつくられるコストは,

v(i) * (それぞれの牛との距離)

というようになります。また, それぞれの牛との距離というのは, i 番目の牛より左側にいる牛の数を lnum, 右側にいる牛の数を rnum, i 番目の牛より左側にいる牛の座標値の合計を lsum, 左側にいる牛の座標値の合計を rsum とすると,

(lnum * xi - lsum) + (rnum * xi - rsum)

というように簡単に計算が出来ます。ということで, 各座標値 x について, 「x より左側にいる牛の数」および「x より左側にいる牛の座標値の合計」を求める BIT を用意しておけば, O(n\log n)で答えを求めることが出来ます。

const int MAXN = 20020;
pair<ll, ll> P[MAXN];
int N;

struct BIT {
    int n;
    vector<ll> bit;
    BIT(int n) : n(n), bit(n+1, 0) {}
    ll sum(int i) {
        ll ret = 0;
        while (i) {
            ret += bit[i];
            i -= i&-i;
        }
        return ret;
    }
    void add(int i, ll x) {
        while (i <= n) {
            bit[i] += x;
            i += i&-i;
        }
    }
};

int main() {
    scanf("%d", &N);
    for (int i = 0; i < N; i++) scanf("%lld %lld", &P[i].first, &P[i].second);
    sort(P, P+N);
    ll ans = 0;
    // b1: i の左側にいる牛の数
    // b2: 座標 i より左側にいる牛の座標値の合計
    BIT b1(MAXN), b2(MAXN);
    for (int i = 0; i < N; i++) {
        pair<ll, ll> p = P[i];
        ll lnum = b1.sum((int)p.second);
        ll rnum = i-lnum;
        ll lsum = b2.sum((int)p.second);
        ll rsum = b2.sum(MAXN)-b2.sum((int)p.second);
        ans += p.first * (lnum*p.second - lsum);
        ans += p.first * (rsum-rnum*p.second);
        b1.add((int)p.second, 1);
        b2.add((int)p.second, p.second);
    }
    cout << ans << endl;
    return 0;
}