LG P1721 [NOI2016]国王饮水记

题面描述

跳蚤国有\(n\)个城市,伟大的跳蚤国王居住在跳蚤国首都中,即 \(1\)号城市中。
跳蚤国最大的问题就是饮水问题,由于首都中居住的跳蚤实在太多,跳蚤国王又体恤地将分配给他的水也给跳蚤国居民饮用,这导致跳蚤国王也经常喝不上水。
于是,跳蚤国在每个城市都修建了一个圆柱形水箱,这些水箱完全相同且足够高。一个雨天后,第\(i\)个城市收集到了高度为\(h_i\) 的水。由于地理和天气因素的影响,任何两个不同城市收集到的水高度互不相同。
跳蚤国王也请来蚂蚁工匠帮忙,建立了一个庞大的地下连通系统。跳蚤国王每次使用地下连通系统时,可以指定任意多的城市,将这些城市的水箱用地下连通系统连接起来足够长的时间之后,再将地下连通系统关闭。由连通器原理,这些城市的水箱中的水在这次操作后会到达同一高度,并且这一高度等于指定的各水箱高度的平均值。
由于地下连通系统的复杂性,跳蚤国王至多只能使用 \(k\)次地下连通系统。跳蚤国王请你告诉他,首都$ 1$号城市水箱中的水位最高能有多高?

分析

\(1.\)所有积水高度小于等于\(1\)号点的点可以直接丢掉. 所以,将留下来的水的高度都改成其原本的高度\(-1\)号点高度,最后答案再加上\(1\)号点的高度.

\(2.\)假如被要求进行两次合并,有两杯水\(h _ 1<h _ 2\),则一定先合并低的,再合并高的. 证明:先合并低的:\(\frac{1}{2}(\frac{1}{2}h_1+h _ 2)=\frac{1}{4}h_1+\frac{1}{2}h_2\),先合并高的:\(\frac{1}{2}h_1+\frac{1}{4}h _ 2\).

\(3.\)若有一部分水合并了,一部分没有,那么被合并的水一定是最高的几杯.

将所有留下来的水从低到高排序,设\(h_i\)表示第\(i\)杯水的高度,\(s _ i\)表示前\(i\)杯水的高度前缀和.

合并操作一定是堆在最后面的一段一段的区间,从前往后合并.

\(dp[i][j]\)表示前j杯水合并了\(i\)次的最大价值,则
\[ dp[i][j]=\max\left\{\frac{dp[i-1][k]+s _ j-s _ k}{j-k+1}\right\} \]
观察这个式子,发现是点\((k-1,s _ k - dp[i-1][k])\)和点\((j, s _ j)\)构成的直线的斜率.

用单调队列维护点\((k-1,s _ k - dp[i-][k])\)构成的下凸壳.因为点\((j, s _ j)\)\(y\)坐标随\(x\)坐标递增,所以找最优决策时,将队首一段不会构成最优决策的点的弹出,队首就是最优决策.

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cstdlib>
#include<algorithm>

using namespace std;

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;

        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);

        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;

    while (*s != 0)
    {
        if (*s == '-')
        {
            is_neg = true;
            ++s;
            break;
        }
        else if (*s >= 48 && *s <= 57)
        {
            break;
        }
        ++s;
    }

    while (*s >= 48 && *s <= 57)
    {
        integer = integer * 10 + *s - 48;
        ++s;
    }

    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++)
    {
        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)
    {
        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
    {
        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)
    {
        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
    {
        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)
    {
        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
    {
        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)
    {
        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
    {
        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;
    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)
    {
        return a - -b;
    }
    else
    {
        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)
    {
        return -b - -a;
    }
    else if (a.is_neg)
    {
        return -(-a + b);
    }
    else
    {
        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;
}

/*以上为高精度浮点数模板*/

const int Maxn=8007;

inline int input()
{
    register int x=0,c=getchar(),s=1;
    
    while(c<'0'||c>'9')
        (c=='-')&&(s=-1),
        c=getchar();
    
    while(c>='0'&&c<='9')
        x=(x<<1)+(x<<3)+(c^48),
        c=getchar();
    
    return c*s;
}


Decimal ans;

int n,K,p,h[Maxn],zy[Maxn][15],s[Maxn],tot;
int Q[Maxn],H,T;
double dp[Maxn][15];

struct Point
{
    double x,y;
}q[Maxn];

inline double slope(Point a,Point b)
{
    return (a.y-b.y)/(a.x-b.x);
}

inline Decimal calc(int i,int j)
{
    if(!j)
        return h[1];
        
    return (calc(zy[i][j],j-1)+s[i]-s[zy[i][j]])/(i-zy[i][j]+1);
}
int main()
{
    n=input();
    K=input();
    p=input();
    h[tot=1]=input();
    
    for(int i=2;i<=n;++i)
    {
        h[i]=input();
        
        if(h[i]>h[1])
            h[++tot]=h[i];
    }
    
    n=tot;
    sort(&h[1],&h[n+1]);
    
    for(int i=1;i<=n;++i)
        s[i]=s[i-1]+h[i];
        
    K=min(K,n);
    
    for(int i=1;i<=n;++i)
        dp[i][0]=h[1];
        
    int lim=min(K,14);
    
    for(int j=1;j<=lim;++j)
    {
        Q[H=T=1]=1;
        
        for(int i=1;i<=n;++i)
            q[i]=(Point)
            {
                i-1,s[i]-dp[i][j-1]
            };
            
        for(int i=2;i<=n;++i)
        {
            Point u=(Point)
            {
                i,s[i]
            };
            
            while(H<T&&slope(u,q[Q[H]])<slope(u,q[Q[H+1]]))
                ++H;
            
            zy[i][j]=Q[H];
            dp[i][j]=(s[i]-s[Q[H]]+dp[Q[H]][j-1])/(i-Q[H]+1);
            
            while(H<T&&slope(q[Q[T]],q[Q[T-1]])>slope(q[Q[T]],q[i]))
                --T;
                
            Q[++T]=i;
        }
    }
    int m=n-K+lim,pos;
    double mx=0;
    
    for(int j=0;j<=lim;++j)
        if(dp[m][j]>mx)
            mx=dp[m][j],
            pos=j;
            
    ans=calc(m,pos);
    
    for(int i=m+1;i<=n;++i)
        ans=(ans+h[i])/2;
        
    cout<<ans.to_string(p<<1)<<endl;
}

转载于:https://www.cnblogs.com/Anverking/p/solution-lgp1721.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值