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;
}
View Code

 

posted @ 2018-10-14 10:34  CzxingcHen  阅读(185)  评论(0编辑  收藏  举报