N 頭牛(N ≤ 20,000)站在數軸上(位置 1..20,000),每牛有聽力閾值 v[i]。任意兩牛 i,j 對話音量 = |x[i]-x[j]| * max(v[i], v[j])。求所有牛對音量之和。
按 v 升序處理牛。處理第 i 牛時,之前牛的 v 均 ≤ 當前 v[i],故雙向貢獻公式統一為:
貢獻 = v[i] * ( Σ|x[i] - x[j]| ),其中 j 為已處理牛。
設已處理牛座標集合為 S,x[i] 為當前座標:
Σ_{j∈S} |x[i] - x[j]| = (cntL * x[i] - sumL) + (sumR - cntR * x[i])
cntL, sumL:座標 ≤ x[i] 的牛數與座標和cntR, sumR:座標 > x[i] 的牛數與座標和兩個樹狀數組維護:BIT1 存牛數,BIT2 存座標和。
#include
using namespace std;
using ll = long long;
const int MAXX = 20000;
struct Cow { int v, x; };
Cow cows[20005];
ll bitCnt[MAXX+5], bitSum[MAXX+5];
void add(ll bit[], int idx, ll val) {
for (; idx <= MAXX; idx += idx & -idx) bit[idx] += val;
}
ll query(ll bit[], int idx) {
ll res = 0;
for (; idx; idx -= idx & -idx) res += bit[idx];
return res;
}
int main() {
int n; scanf("%d", &n);
for (int i=1; i<=n; ++i) scanf("%d%d", &cows[i].v, &cows[i].x);
sort(cows+1, cows+n+1, [](Cow a, Cow b){ return a.v < b.v; });
ll ans = 0;
for (int i=1; i<=n; ++i) {
int x = cows[i].x;
ll v = cows[i].v;
ll cntL = query(bitCnt, x);
ll sumL = query(bitSum, x);
ll cntR = (i-1) - cntL;
ll sumR = query(bitSum, MAXX) - sumL;
ll contrib = v * (cntL * x - sumL + sumR - cntR * x);
ans += contrib;
add(bitCnt, x, 1);
add(bitSum, x, x);
}
printf("%lld\n", ans);
return 0;
}
輸入:
4 3 1 2 5 2 6 4 3
按 v 排序:(2,5), (2,6), (3,1), (4,3)
| 步驟 | 牛 | 已處理座標 | 貢獻計算 | 累計 |
|---|---|---|---|---|
| 1 | (2,5) | ∅ | 0 | 0 |
| 2 | (2,6) | {5} | 2×|6-5|=2 | 2 |
| 3 | (3,1) | {5,6} | 3×(|1-5|+|1-6|)=3×9=27 | 29 |
| 4 | (4,3) | {5,6,1} | 4×(|3-5|+|3-6|+|3-1|)=4×7=28 | 57 |
輸出 57 ✓
v 排序將 max(v[i],v[j]) 統一為當前 v[i]POJ 1990 是「排序 + 樹狀數組維護前綴統計」的經典應用。核心技巧:將雙重循環的非對稱 max 轉化為單向掃描的對稱貢獻,利用數據結構將 O(N²) 優化至 O(N log N)。此模式廣泛適用於「按某屬性排序後計算配對貢獻」的問題。