hdu - 4319 - Subsequence Problem - 凸包

给个序列,求 (Ai + Ai+1 + ... + Aj)^2 / (j - i + 1). (1 <= i <= j <= n)

解:http://acm.hdu.edu.cn/showproblem.php?pid=4319

难题。用分治法来做,关键在于O(n)时间完成合并操作。我们把要求的结果开方,答案不变,即求max{abs(a[i]+...+a[j]) / (j-i+1)}. 设函数f(x,y) = |y| / sqrt(x),则x即为子序列长度,y即为子序列和,下面我们可以把(x,y)看作平面内的点。

 

欲处理a[i..j]之内的最优区间,递归处理a[i..mid]和a[(mid+1)...j],然后设

b1 = (1, a_mid), b2 = (2, a_mid + a_(mid-1)), b3 = (3, a_mid + a_(mid-1) + a_(mid-2))...

c1 = (1, a_(mid+1)), c2 = (2, a_(mid+1)+a_(mid+2)) ...

我们把bi, ci都看作二维平面内的点,分别计算出bi, ci的上凸包和下凸包,再把两个上凸包合并,两个下凸包合并,最优解一定在合并得到的凸包上。这个过程是可以O(n)完成的。

 

两个上凸包p,q合并成t是指如下过程:

t1 = p1 + q1

如果ti = pj + qk, 那么t(i+1) = p(j+1) + qk 或 t(i+1) = pj + q(k+1),根据凸性决定保留哪一个

下凸包的合并也是类似过程。


#include <cstdio>
#include <cstring>
#include <cmath>
const int MAXN = 100010;

struct Point
{
    __int64 x, y;
}point[MAXN], high[2][MAXN], low[2][MAXN], merge[MAXN * 2];
int pointNumber, highNumber[2], lowNumber[2], mergeNumber;
int n, stack[MAXN], top;
__int64 a[MAXN];

Point operator + (const Point &a, const Point &b)
{
    Point r;
    r.x = a.x + b.x;
    r.y = a.y + b.y;
    return r;
}

Point operator - (const Point &a, const Point &b)
{
    Point r;
    r.x = a.x - b.x;
    r.y = a.y - b.y;
    return r;
}

inline __int64 operator * (const Point &a, const Point &b)
{
    return a.x * b.y - a.y * b.x;
}

inline __int64 absolute(int x)
{
    return x >= 0 ? x : -x;
}

inline double max(const double &x, const double &y)
{
    return x > y ? x : y;
}

inline __int64 cross(const Point &a, const Point &b, const Point &o)
{
    return (a - o) * (b - o);
}

void getBound(Point result[], int &resultNumber, bool isHigh)
{
    if(pointNumber == 1)
    {
        result[0] = point[0];
        resultNumber = 1;
        return;
    }
    stack[0] = 0;
    stack[1] = 1;
    top = 2;
    if(isHigh)
    {
        for(int i=2;i<pointNumber;++i)
        {
            while(top >= 2 && cross(point[stack[top-2]], point[i], point[stack[top-1]]) < 0)
            {
                -- top;
            }
            stack[top++] = i;
        }
    }
    else
    {
        for(int i=2;i<pointNumber;++i)
        {
            while(top >= 2 && cross(point[stack[top-2]], point[i], point[stack[top-1]]) > 0)
            {
                -- top;
            }
            stack[top++] = i;
        }
    }
    resultNumber = top;
    for(int i=0;i<top;++i)
    {
        result[i].x = point[stack[i]].x;
        result[i].y = point[stack[i]].y;
    }
}

void mergeBound(Point p1[], int len1, Point p2[], int len2, bool isHigh)
{
    merge[mergeNumber++] = p1[0] + p2[0];
    int pNum1 = 1, pNum2 = 1;
    while(pNum1 < len1 || pNum2 < len2)
    {
        if(pNum1 == len1)
        {
            merge[mergeNumber++] = p1[pNum1 - 1] + p2[pNum2 ++];
        }
        else if(pNum2 == len2)
        {
            merge[mergeNumber++] = p1[pNum1 ++] + p2[pNum2 - 1];
        }
        else
        {
            Point temp0 = p1[pNum1 - 1] + p2[pNum2 - 1];
            Point temp1 = p1[pNum1] + p2[pNum2 - 1];
            Point temp2 = p1[pNum1 - 1] + p2[pNum2];
            if(isHigh)
            {
                if(cross(temp1, temp2, temp0) < 0)
                {
                    merge[mergeNumber++] = temp1;
                    ++ pNum1;
                }
                else
                {
                    merge[mergeNumber++] = temp2;
                    ++ pNum2;
                }
            }
            else
            {
                if(cross(temp1, temp2, temp0) > 0)
                {
                    merge[mergeNumber++] = temp1;
                    ++ pNum1;
                }
                else
                {
                    merge[mergeNumber++] = temp2;
                    ++ pNum2;
                }
            }
        }
    }
}

double solve(int l, int r)
{
    if(l == r)
    {
        return absolute(a[l]);
    }
    int mid = (l + r) >> 1;
    double ansL = solve(l, mid);
    double ansR = solve(mid + 1, r);
    double ans = max(ansL, ansR);
    __int64 sum = 0;
    pointNumber = 0;
    for(int i=mid;i>=l;--i)
    {
        sum += a[i];
        point[pointNumber].x = mid - i + 1;
        point[pointNumber].y = sum;
        ++ pointNumber;
    }
    getBound(high[0], highNumber[0], true);
    getBound(low[0], lowNumber[0], false);
    sum = 0;
    pointNumber = 0;
    for(int i=mid+1;i<=r;++i)
    {
        sum += a[i];
        point[pointNumber].x = i - mid;
        point[pointNumber].y = sum;
        ++ pointNumber;
    }
    getBound(high[1], highNumber[1], true);
    getBound(low[1], lowNumber[1], false);
    mergeNumber = 0;
    mergeBound(high[0], highNumber[0], high[1], highNumber[1], true);
    mergeBound(low[0], lowNumber[0], low[1], lowNumber[1], false);
    for(int i=0;i<mergeNumber;++i)
    {
        ans = max(ans, fabs((double)merge[i].y) / sqrt((double)merge[i].x));
    }
    return ans;
}

int main()
{
    while(~scanf("%d", &n))
    {
        for(int i=0;i<n;++i)
        {
            scanf("%I64d", &a[i]);
        }
        double ans = solve(0, n - 1);
        printf("%.6lf\n", ans * ans);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值