Luogu 3466 [POI2008]KLO-Building blocks
BZOJ 1112。
题意相当于在一个长度为$k$的区间内选择一个数$s$使$\sum_{i = 1}^{k}\left | a_i - s \right |$最小。
很显然是中位数。
然后只要写一个能查询长度为$k$的区间的中位数,以及小于和大于这个中位数的总和和个数的数据结构即可。
线段树平衡树对顶堆随便维护。
我选择权值线段树。
时间复杂度$O(nlogn)$。
Luogu上还需要输出方案。
Code:
#include <cstdio> #include <cstring> #include <algorithm> using namespace std; typedef long long ll; const int N = 1e5 + 5; int n, K; ll mn = 0LL, a[N]; template <typename T> inline void read(T &X) { X = 0; char ch = 0; T op = 1; for(; ch > '9' || ch < '0'; ch = getchar()) if(ch == '-') op = -1; for(; ch >= '0' && ch <= '9'; ch = getchar()) X = (X << 3) + (X << 1) + ch - 48; X *= op; } template <typename T> inline void chkMax(T &x, T y) { if(y > x) x = y; } namespace SegT { struct Node { int lc, rc; ll sum, cnt; } s[N * 40]; int root, nodeCnt = 0; #define lc(p) s[p].lc #define rc(p) s[p].rc #define sum(p) s[p].sum #define cnt(p) s[p].cnt #define mid ((l + r) >> 1) void ins(int &p, ll l, ll r, ll x) { if(!p) p = ++nodeCnt; sum(p) += x, ++cnt(p); if(l == r) return; if(x <= mid) ins(lc(p), l, mid, x); else ins(rc(p), mid + 1, r, x); } void del(int &p, ll l, ll r, ll x) { sum(p) -= x, --cnt(p); if(l == r) return; if(x <= mid) del(lc(p), l, mid, x); else del(rc(p), mid + 1, r, x); } ll getKth(int p, ll l, ll r, int k) { if(l == r) return l; int now = cnt(lc(p)); if(k <= now) return getKth(lc(p), l, mid, k); else return getKth(rc(p), mid + 1, r, k - now); } int qCnt(int p, ll l, ll r, ll x, ll y) { if(x <= l && y >= r) return cnt(p); int res = 0; if(x <= mid) res += qCnt(lc(p), l, mid, x, y); if(y > mid) res += qCnt(rc(p), mid + 1, r, x, y); return res; } ll qSum(int p, ll l, ll r, ll x, ll y) { if(x <= l && y >= r) return sum(p); ll res = 0LL; if(x <= mid) res += qSum(lc(p), l, mid, x, y); if(y > mid) res += qSum(rc(p), mid + 1, r, x, y); return res; } #undef mid } using namespace SegT; int main() { read(n), read(K); for(int i = 1; i <= n; i++) { read(a[i]); chkMax(mn, a[i]); } // ll sum = 0LL; for(int i = 1; i <= K; i++) { // sum += a[i]; ins(root, 0, mn, a[i]); } int pos = 1; ll mid = getKth(root, 0, mn, (K + 1) / 2); ll minCost = mid * qCnt(root, 0, mn, 0, mid) - qSum(root, 0, mn, 0, mid); minCost += qSum(root, 0, mn, mid + 1, mn) - mid * qCnt(root, 0, mn, mid + 1, mn); for(int i = K + 1; i <= n; i++) { del(root, 0, mn, a[i - K]); ins(root, 0, mn, a[i]); ll nowMid = getKth(root, 0, mn, (K + 1) / 2); ll nowCost = nowMid * qCnt(root, 0, mn, 0, nowMid) - qSum(root, 0, mn, 0, nowMid); nowCost += qSum(root, 0, mn, nowMid + 1, mn) - nowMid * qCnt(root, 0, mn, nowMid + 1, mn); if(nowCost < minCost) { pos = i - K + 1; mid = nowMid; minCost = nowCost; } } printf("%lld\n", minCost); /* for(int i = 1; i <= n; i++) { if(i >= pos && i <= pos + K - 1) printf("%lld\n", mid); else printf("%lld\n", a[i]); } */ return 0; }