uoj233/BZOJ4654/洛谷P1721 [Noi2016]国王饮水记 【dp + 斜率优化】

题目链接

uoj233

题解

下面不加证明地给出几个性质:

  1. 小于\(h[1]\)的城市一定是没用的
  2. 任何城市联通包含\(1\)且只和\(1\)联通一次
  3. 联通顺序从小到大最优
  4. 单个联通比多个一起联通要优
  5. 最优解中多个一起联通不超过\(14\)

除了最后一个外还是很显然的
\(K\)足够大肯定逐个联通,否则有一些还是需要一起联通的
我们可以设\(f[i][j]\)表示前\(i\)个城市,联通\(j\)\(h[1]\)的最高高度
那么,去掉小于\(h[1]\)的城市,将\(h[i]\)排序,记\(s[i]\)\(h[i]\)前缀和
\[f[i][j] = max\{\frac{f[k][j - 1] + s[i] - s[k]}{i - k + 1}\}\]
可以斜率优化且具有决策单调性
又由于最多不超过\(14\)次多个联通,所以找到\(14\)次中最优决策之后剩余的就可以逐个联通了
复杂度\(O(np)\)

#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;

// ---------- decimal lib start ----------
const int PREC = 3000;
class Decimal {
    public:
        Decimal();
        Decimal(const std::string &s);
        Decimal(const char *s);
        Decimal(int x);
        Decimal(long long x);
        Decimal(double x);
        
        bool is_zero() const;
        
        // p (p > 0) is the number of digits after the decimal point
        std::string to_string(int p) const;
        double to_double() const;
        
        friend Decimal operator + (const Decimal &a, const Decimal &b);
        friend Decimal operator + (const Decimal &a, int x);
        friend Decimal operator + (int x, const Decimal &a);
        friend Decimal operator + (const Decimal &a, long long x);
        friend Decimal operator + (long long x, const Decimal &a);
        friend Decimal operator + (const Decimal &a, double x);
        friend Decimal operator + (double x, const Decimal &a);
        
        friend Decimal operator - (const Decimal &a, const Decimal &b);
        friend Decimal operator - (const Decimal &a, int x);
        friend Decimal operator - (int x, const Decimal &a);
        friend Decimal operator - (const Decimal &a, long long x);
        friend Decimal operator - (long long x, const Decimal &a);
        friend Decimal operator - (const Decimal &a, double x);
        friend Decimal operator - (double x, const Decimal &a);
        
        friend Decimal operator * (const Decimal &a, int x);
        friend Decimal operator * (int x, const Decimal &a);
        
        friend Decimal operator / (const Decimal &a, int x);
        
        friend bool operator < (const Decimal &a, const Decimal &b);
        friend bool operator > (const Decimal &a, const Decimal &b);
        friend bool operator <= (const Decimal &a, const Decimal &b);
        friend bool operator >= (const Decimal &a, const Decimal &b);
        friend bool operator == (const Decimal &a, const Decimal &b);
        friend bool operator != (const Decimal &a, const Decimal &b);
        
        Decimal & operator += (int x);
        Decimal & operator += (long long x);
        Decimal & operator += (double x);
        Decimal & operator += (const Decimal &b);
        
        Decimal & operator -= (int x);
        Decimal & operator -= (long long x);
        Decimal & operator -= (double x);
        Decimal & operator -= (const Decimal &b);
        
        Decimal & operator *= (int x);
        
        Decimal & operator /= (int x);
        
        friend Decimal operator - (const Decimal &a);
        
        // These can't be called
        friend Decimal operator * (const Decimal &a, double x);
        friend Decimal operator * (double x, const Decimal &a);
        friend Decimal operator / (const Decimal &a, double x);
        Decimal & operator *= (double x);
        Decimal & operator /= (double x);
        
    private:
        static const int len = PREC / 9 + 1;
        static const int mo = 1000000000;
        
        static void append_to_string(std::string &s, long long x);
        
        bool is_neg;
        long long integer;
        int data[len];
        
        void init_zero();
        void init(const char *s);
};

Decimal::Decimal() {
    this->init_zero();
}

Decimal::Decimal(const char *s) {
    this->init(s);
}

Decimal::Decimal(const std::string &s) {
    this->init(s.c_str());
}

Decimal::Decimal(int x) {
    this->init_zero();
    
    if (x < 0) {
        is_neg = true;
        x = -x;
    }
    
    integer = x;
}

Decimal::Decimal(long long x) {
    this->init_zero();
    
    if (x < 0) {
        is_neg = true;
        x = -x;
    }
    
    integer = x;
}

Decimal::Decimal(double x) {
    this->init_zero();
    
    if (x < 0) {
        is_neg = true;
        x = -x;
    }
    
    integer = (long long)x;
    x -= integer;
    
    for (int i = 0; i < len; i++) {
        x *= mo;
        if (x < 0) x = 0;
        data[i] = (int)x;
        x -= data[i];
    }
}

void Decimal::init_zero() {
    is_neg = false;
    integer = 0;
    memset(data, 0, len * sizeof(int));
}

bool Decimal::is_zero() const {
    if (integer) return false;
    for (int i = 0; i < len; i++) {
        if (data[i]) return false;
    }
    return true;
}

void Decimal::init(const char *s) {
    this->init_zero();
    
    is_neg = false;
    integer = 0;
    
    // find the first digit or the negative sign
    while (*s != 0) {
        if (*s == '-') {
            is_neg = true;
            ++s;
            break;
        } else if (*s >= 48 && *s <= 57) {
            break;
        }
        ++s;
    }
    
    // read the integer part
    while (*s >= 48 && *s <= 57) {
        integer = integer * 10 + *s - 48;
        ++s;
    }
    
    // read the decimal part
    if (*s == '.') {
        int pos = 0;
        int x = mo / 10;
        
        ++s;
        while (pos < len && *s >= 48 && *s <= 57) {
            data[pos] += (*s - 48) * x;
            ++s;
            x /= 10;
            if (x == 0) {
                ++pos;
                x = mo / 10;
            }
        }
    }
}

void Decimal::append_to_string(std::string &s, long long x) {
    if (x == 0) {
        s.append(1, 48);
        return;
    }
    
    char _[30];
    int cnt = 0;
    while (x) {
        _[cnt++] = x % 10;
        x /= 10;
    }
    while (cnt--) {
        s.append(1, _[cnt] + 48);
    }
}

std::string Decimal::to_string(int p) const {
    std::string ret;
    
    if (is_neg && !this->is_zero()) {
        ret = "-";
    }
    
    append_to_string(ret, this->integer);
    
    ret.append(1, '.');
    
    for (int i = 0; i < len; i++) {
        // append data[i] as "%09d"
        int x = mo / 10;
        int tmp = data[i];
        while (x) {
            ret.append(1, 48 + tmp / x);
            tmp %= x;
            x /= 10;
            if (--p == 0) {
                break;
            }
        }
        if (p == 0) break;
    }
    
    if (p > 0) {
        ret.append(p, '0');
    }
    
    return ret;
}

double Decimal::to_double() const {
    double ret = integer;
    
    double k = 1.0;
    for (int i = 0; i < len; i++) {
        k /= mo;
        ret += k * data[i];
    }
    
    if (is_neg) {
        ret = -ret;
    }
    
    return ret;
}

bool operator < (const Decimal &a, const Decimal &b) {
    if (a.is_neg != b.is_neg) {
        return a.is_neg && (!a.is_zero() || !b.is_zero());
    } else if (!a.is_neg) {
        // a, b >= 0
        if (a.integer != b.integer) {
            return a.integer < b.integer;
        }
        for (int i = 0; i < Decimal::len; i++) {
            if (a.data[i] != b.data[i]) {
                return a.data[i] < b.data[i];
            }
        }
        return false;
    } else {
        // a, b <= 0
        if (a.integer != b.integer) {
            return a.integer > b.integer;
        }
        for (int i = 0; i < Decimal::len; i++) {
            if (a.data[i] != b.data[i]) {
                return a.data[i] > b.data[i];
            }
        }
        return false;
    }
}

bool operator > (const Decimal &a, const Decimal &b) {
    if (a.is_neg != b.is_neg) {
        return !a.is_neg && (!a.is_zero() || !b.is_zero());
    } else if (!a.is_neg) {
        // a, b >= 0
        if (a.integer != b.integer) {
            return a.integer > b.integer;
        }
        for (int i = 0; i < Decimal::len; i++) {
            if (a.data[i] != b.data[i]) {
                return a.data[i] > b.data[i];
            }
        }
        return false;
    } else {
        // a, b <= 0
        if (a.integer != b.integer) {
            return a.integer < b.integer;
        }
        for (int i = 0; i < Decimal::len; i++) {
            if (a.data[i] != b.data[i]) {
                return a.data[i] < b.data[i];
            }
        }
        return false;
    }
}

bool operator <= (const Decimal &a, const Decimal &b) {
    if (a.is_neg != b.is_neg) {
        return a.is_neg || (a.is_zero() && b.is_zero());
    } else if (!a.is_neg) {
        // a, b >= 0
        if (a.integer != b.integer) {
            return a.integer < b.integer;
        }
        for (int i = 0; i < Decimal::len; i++) {
            if (a.data[i] != b.data[i]) {
                return a.data[i] < b.data[i];
            }
        }
        return true;
    } else {
        // a, b <= 0
        if (a.integer != b.integer) {
            return a.integer > b.integer;
        }
        for (int i = 0; i < Decimal::len; i++) {
            if (a.data[i] != b.data[i]) {
                return a.data[i] > b.data[i];
            }
        }
        return true;
    }
}

bool operator >= (const Decimal &a, const Decimal &b) {
    if (a.is_neg != b.is_neg) {
        return !a.is_neg || (a.is_zero() && b.is_zero());
    } else if (!a.is_neg) {
        // a, b >= 0
        if (a.integer != b.integer) {
            return a.integer > b.integer;
        }
        for (int i = 0; i < Decimal::len; i++) {
            if (a.data[i] != b.data[i]) {
                return a.data[i] > b.data[i];
            }
        }
        return true;
    } else {
        // a, b <= 0
        if (a.integer != b.integer) {
            return a.integer < b.integer;
        }
        for (int i = 0; i < Decimal::len; i++) {
            if (a.data[i] != b.data[i]) {
                return a.data[i] < b.data[i];
            }
        }
        return true;
    }
}

bool operator == (const Decimal &a, const Decimal &b) {
    if (a.is_zero() && b.is_zero()) return true;
    if (a.is_neg != b.is_neg) return false;
    if (a.integer != b.integer) return false;
    for (int i = 0; i < Decimal::len; i++) {
        if (a.data[i] != b.data[i]) return false;
    }
    return true;
}

bool operator != (const Decimal &a, const Decimal &b) {
    return !(a == b);
}

Decimal & Decimal::operator += (long long x) {
    if (!is_neg) {
        if (integer + x >= 0) {
            integer += x;
        } else {
            bool last = false;
            for (int i = len - 1; i >= 0; i--) {
                if (last || data[i]) {
                    data[i] = mo - data[i] - last;
                    last = true;
                } else {
                    last = false;
                }
            }
            integer = -x - integer - last;
            is_neg = true;
        }
    } else {
        if (integer - x >= 0) {
            integer -= x;
        } else {
            bool last = false;
            for (int i = len - 1; i >= 0; i--) {
                if (last || data[i]) {
                    data[i] = mo - data[i] - last;
                    last = true;
                } else {
                    last = false;
                }
            }
            integer = x - integer - last;
            is_neg = false;
        }
    }
    return *this;
}

Decimal & Decimal::operator += (int x) {
    return *this += (long long)x;
}

Decimal & Decimal::operator -= (int x) {
    return *this += (long long)-x;
}

Decimal & Decimal::operator -= (long long x) {
    return *this += -x;
}

Decimal & Decimal::operator /= (int x) {
    if (x < 0) {
        is_neg ^= 1;
        x = -x;
    }
    
    int last = integer % x;
    integer /= x;
    
    for (int i = 0; i < len; i++) {
        long long tmp = 1LL * last * mo + data[i];
        data[i] = tmp / x;
        last = tmp - 1LL * data[i] * x;
    }
    
    if (is_neg && integer == 0) {
        int i;
        for (i = 0; i < len; i++) {
            if (data[i] != 0) {
                break;
            }
        }
        if (i == len) {
            is_neg = false;
        }
    }
    
    return *this;
}

Decimal & Decimal::operator *= (int x) {
    if (x < 0) {
        is_neg ^= 1;
        x = -x;
    } else if (x == 0) {
        init_zero();
        return *this;
    }
    
    int last = 0;
    for (int i = len - 1; i >= 0; i--) {
        long long tmp = 1LL * data[i] * x + last;
        last = tmp / mo;
        data[i] = tmp - 1LL * last * mo;
    }
    integer = integer * x + last;
    
    return *this;
}

Decimal operator - (const Decimal &a) {
    Decimal ret = a;
    // -0 = 0
    if (!ret.is_neg && ret.integer == 0) {
        int i;
        for (i = 0; i < Decimal::len; i++) {
            if (ret.data[i] != 0) break;
        }
        if (i < Decimal::len) {
            ret.is_neg = true;
        }
    } else {
        ret.is_neg ^= 1;
    }
    return ret;
}

Decimal operator + (const Decimal &a, int x) {
    Decimal ret = a;
    return ret += x;
}

Decimal operator + (int x, const Decimal &a) {
    Decimal ret = a;
    return ret += x;
}

Decimal operator + (const Decimal &a, long long x) {
    Decimal ret = a;
    return ret += x;
}

Decimal operator + (long long x, const Decimal &a) {
    Decimal ret = a;
    return ret += x;
}

Decimal operator - (const Decimal &a, int x) {
    Decimal ret = a;
    return ret -= x;
}

Decimal operator - (int x, const Decimal &a) {
    return -(a - x);
}

Decimal operator - (const Decimal &a, long long x) {
    Decimal ret = a;
    return ret -= x;
}

Decimal operator - (long long x, const Decimal &a) {
    return -(a - x);
}

Decimal operator * (const Decimal &a, int x) {
    Decimal ret = a;
    return ret *= x;
}

Decimal operator * (int x, const Decimal &a) {
    Decimal ret = a;
    return ret *= x;
}

Decimal operator / (const Decimal &a, int x) {
    Decimal ret = a;
    return ret /= x;
}

Decimal operator + (const Decimal &a, const Decimal &b) {
    if (a.is_neg == b.is_neg) {
        Decimal ret = a;
        bool last = false;
        for (int i = Decimal::len - 1; i >= 0; i--) {
            ret.data[i] += b.data[i] + last;
            if (ret.data[i] >= Decimal::mo) {
                ret.data[i] -= Decimal::mo;
                last = true;
            } else {
                last = false;
            }
        }
        ret.integer += b.integer + last;
        return ret;
    } else if (!a.is_neg) {
        // a - |b|
        return a - -b;
    } else {
        // b - |a|
        return b - -a;
    }
}

Decimal operator - (const Decimal &a, const Decimal &b) {
    if (!a.is_neg && !b.is_neg) {
        if (a >= b) {
            Decimal ret = a;
            bool last = false;
            for (int i = Decimal::len - 1; i >= 0; i--) {
                ret.data[i] -= b.data[i] + last;
                if (ret.data[i] < 0) {
                    ret.data[i] += Decimal::mo;
                    last = true;
                } else {
                    last = false;
                }
            }
            ret.integer -= b.integer + last;
            return ret;
        } else {
            Decimal ret = b;
            bool last = false;
            for (int i = Decimal::len - 1; i >= 0; i--) {
                ret.data[i] -= a.data[i] + last;
                if (ret.data[i] < 0) {
                    ret.data[i] += Decimal::mo;
                    last = true;
                } else {
                    last = false;
                }
            }
            ret.integer -= a.integer + last;
            ret.is_neg = true;
            return ret;
        }
    } else if (a.is_neg && b.is_neg) {
        // a - b = (-b) - (-a)
        return -b - -a;
    } else if (a.is_neg) {
        // -|a| - b
        return -(-a + b);
    } else {
        // a - -|b|
        return a + -b;
    }
}

Decimal operator + (const Decimal &a, double x) {
    return a + Decimal(x);
}

Decimal operator + (double x, const Decimal &a) {
    return Decimal(x) + a;
}

Decimal operator - (const Decimal &a, double x) {
    return a - Decimal(x);
}

Decimal operator - (double x, const Decimal &a) {
    return Decimal(x) - a;
}

Decimal & Decimal::operator += (double x) {
    *this = *this + Decimal(x);
    return *this;
}

Decimal & Decimal::operator -= (double x) {
    *this = *this - Decimal(x);
    return *this;
}

Decimal & Decimal::operator += (const Decimal &b) {
    *this = *this + b;
    return *this;
}

Decimal & Decimal::operator -= (const Decimal &b) {
    *this = *this - b;
    return *this;
}

// ---------- decimal lib end ----------
inline int read(){
    int out = 0,flag = 1; char c = getchar();
    while (c < 48 || c > 57){if (c == '-') flag = 0; c = getchar();}
    while (c >= 48 && c <= 57){out = (out << 1) + (out << 3) + c - 48; c = getchar();}
    return flag ? out : -out;
}
const int maxn = 8005;
Decimal ans;
int n,K,P,tot,h[maxn],pre[maxn][15],S[maxn];
int q[maxn],head,tail;
double f[maxn][15];
struct point{double x,y;}p[maxn];
double slope(const point& a,const point& b){
    return (a.y - b.y) /(a.x - b.x);
}
Decimal calc(int i,int j){
    if (!j) return h[1];
    return (calc(pre[i][j],j - 1) + S[i] - S[pre[i][j]]) / (i - pre[i][j] + 1);
}
int main(){
    //freopen("1.in","r",stdin);
    //freopen("1.out","w",stdout);
    n = read(); K = read(); P = read(); h[tot = 1] = read();
    for (int i = 2; i <= n; i++){
        h[i] = read();
        if (h[i] > h[1]) h[++tot] = h[i];
    }
    n = tot; sort(h + 1,h + 1 + n);
    for (int i = 1; i <= n; i++) S[i] = S[i - 1] + h[i];
    K = min(n,K);
    int E = min(K,14);
    for (int i = 1; i <= n; i++) f[i][0] = h[1];
    for (int j = 1; j <= E; j++){
        f[1][j] = h[1];
        q[head = tail = 1] = 1;
        for (int i = 1; i <= n; i++) p[i] = (point){i - 1,S[i] - f[i][j - 1]};
        for (int i = 2; i <= n; i++){
            point u = (point){i,S[i]};
            while (head < tail && slope(u,p[q[head]]) < slope(u,p[q[head + 1]])) head++;
            pre[i][j] = q[head];
            int v = q[head];
            f[i][j] = (f[v][j - 1] + S[i] - S[v]) / (i - v + 1);
            while (head < tail && slope(p[i],p[q[tail]]) < slope(p[q[tail - 1]],p[q[tail]])) tail--;
            q[++tail] = i;
        }
    }
    int m = n - K + E;
    double mx = 0; int pos;
    for (int i = 0; i <= E; i++)
        if (f[m][i] > mx) mx = f[m][i],pos = m;
    ans = calc(pos,E);
    for (int i = pos + 1; i <= n; i++)
        ans = (ans + h[i]) / 2;
    cout << ans.to_string(P << 1) << endl;
    return 0;
}
posted @ 2018-07-13 15:38 Mychael 阅读(...) 评论(...) 编辑 收藏