中位数及带权中位数题集
1 #include <bits/stdc++.h> 2 using namespace std; 3 int x[11111], y[11111]; 4 int main() 5 { 6 int n; scanf("%d", &n); 7 for(int i = 1; i <= n; i++) scanf("%d %d", &x[i], &y[i]); 8 ///先处理y 9 sort(y + 1, y + n + 1); 10 long long ans = 0; 11 int mid = n / 2 + 1; 12 for(int i = 1; i <= n; i++) ans += abs(y[mid] - y[i]); 13 ///再处理x 14 sort(x + 1, x + n + 1); 15 for(int i = 1; i <= n; i++) x[i] -= (i - 1); 16 sort(x + 1, x + n + 1); 17 for(int i = 1; i <= n; i++) ans += abs(x[mid] - x[i]); 18 printf("%lld\n", ans); 19 return 0; 20 }
题解真的又写的非常清楚了……
自己在想的时候,确实想到肯定有一个块不动,其他的动,我们只需要去移那些贡献小的,我猜是最重的那个,但自己写了组样例就推翻了。
题解讲,用$S(l,r)$表示将索引$(l,r)$物品移动一步的花费,不动的那块应该满足$S(l,k-1)≤ \frac{S(l,r)}{2}$,$S(l,k)> \frac{S(l,r)}{2}$。Q神说是熟知的带权中位数问题。
求$k$的过程,可以用二分+树状数组解决。
求得$k$后,因为$k$是固定不动的,如果$i<k$,那么花费是$\sum_{i \in[l, k-1]} {w_{i}(a_{k}-a_{i}-(k-i))}$,令$a_{i}=a_{i}-i$,那么原式$= \sum_{i \in[l, k-1]} {w_{i}(a_{k}-a_{i})}=a_{k}\times S(l,k-1)-\sum_{i \in[l, k-1]} {w_{i}a_{i}}$,再用一颗树状数组来维护$w_{i} a_{i}$就可以了。$i > k$类似。
真的是慎用除法,不然都不知道怎么死的……
#include <bits/stdc++.h> using namespace std; typedef long long ll; const ll mod = 1e9 + 7; const int maxn = 2e5 + 50; ll a[maxn], w[maxn]; ll C[maxn]; int n, q; void add1(int x, ll v) { while(x <= n) { C[x] += v; x += x & -x; } } ll query1(int x) { ll res = 0; while(x > 0) { res += C[x]; x -= x & -x; } return res; } ll calc(int l, int r) { if(r < l) return 0; ll ans = query1(r) - query1(l - 1); // printf("%lld\n", ans); return ans; } ll D[maxn]; void add2(int x, ll v) { while(x <= n) { D[x] += v; D[x] %= mod; x += x & -x; } } ll query2(int x) { ll res = 0; while(x > 0) { res += D[x]; res %= mod; x -= x & -x; } return res; } ll calc2(int l, int r) { if(r < l) return 0; return (query2(r) - query2(l - 1) + mod) % mod; } int main() { scanf("%d %d", &n, &q); for(int i = 1; i <= n; i++) scanf("%lld", &a[i]); for(int i = 1; i <= n; i++) scanf("%lld", &w[i]), add1(i, w[i]), a[i] = a[i] - i, add2(i, w[i] * a[i] % mod); while(q--) { int l, r; scanf("%d %d", &l, &r); ///首先二分 if(l > 0) { ll S = calc(l, r); int k = 0; int tmpl = l, tmpr = r; while(tmpl <= tmpr) { int mid = (tmpl + tmpr) >> 1; if(calc(l, mid - 1) * 2LL < S && calc(l, mid) * 2LL < S) { tmpl = mid + 1; } else if(calc(l, mid - 1) * 2LL > S && calc(l, mid) * 2LL > S) { tmpr = mid - 1; } else { k = mid; break; } } ///得到k后,开始计算花费 ll ans = (calc(l, k - 1) % mod * a[k] % mod - calc2(l, k - 1) + mod) % mod; ans = (ans + (calc2(k + 1, r) - calc(k + 1, r) % mod * a[k] % mod + mod) % mod) % mod; printf("%lld\n", ans); } else { int id = -l; ll nw = r; add1(id, nw - w[id]), add2(id, (a[id] * nw % mod - a[id] * w[id] % mod + mod) % mod); w[id] = nw; } } int ccc = 0; return 0; }
sgu 114
POJ 1160
ZOJ 1196