51nod 1593 公园晨跑

51nod 1593 公园晨跑

原题链接
https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1593

题目中,猴子消耗的能量等于:
两个点的距离加上两个点上 树的高度的2倍
我们可以把环撕开,变为链,因为路径是环上连续的一段。
任何一段都可以映射到序列:

1,2,3,4,...n,1,2,3,4,...n

把询问环,变为某一个区间

令 ,第i棵树的高度为

hi

令,第1个点在0位置,第i个点相对于第1个点的位置为:

di

如果,选定 x,y作为猴子选择的两个棵树的编号,那么它消耗的能量为:

dxdy+2hx+2hy

x<y

dxdy+2hx+2hy=dydx+2hx+2hy=(dy+2hy)(dx2hx)

令 :

A[x]=dx+2hxB[x]=dx2hx

原问题等价于计算,指定区间 [L,R] 内:

maxx,y[L,R]  ,    xy(A[x]B[y])

假设 x=X,y=Y 时 有:

maxx[L,R]A[x]=A[X]miny[L,R]B[y]=B[Y]

XY 时:

maxx,y[L,R]  ,    xy(A[x]B[y])=A[X]B[Y]

X=Y 时:
假设有 满足下面等式的 a,b

maxx,y[L,R]  ,    xy(A[x]B[y])=A[a]B[b]

如果 aX bY ,又因为 , A[X]>A[a]  B[Y]<B[b]
所以:

A[X]B[b]>A[a]B[b]A[a]B[Y]>A[a]B[b]

与上文矛盾。
所以有:
a=X    b=Y  ,

所以,利用线段树求出区间最大值(A中)与最小值(B中)的下标,

如果两个最值在同一个位置,即 X=Y 则枚举:

a=X 时的答案,有:

B[b]=miny[L,R],yYB[y]

b=Y 时的答案,有:

A[a]=maxx[L,R],xXB[x]

#include <stdio.h>
#include <algorithm>
#include <string.h>
#define MAXN 300005
using namespace std;
typedef long long LL;
typedef pair<LL,LL>P;
struct point
{
    int c[2];
    LL ma;
    int x;
    point()
    {
        c[0]=c[1]=0;
        ma=-0x3f3f3f3f3f3f3f3f;
    }
};

struct Bt
{
    point A[MAXN*8];
    int deep;
    int root;
    LL ma;
    LL key;
    int xx;
    int n;
    Bt()
    {
        root=1;
        deep=2;
    }
    void update(point &a)
    {
        if(A[a.c[0]].ma>A[a.c[1]].ma)
        {
            a.ma=A[a.c[0]].ma;
            a.x=A[a.c[0]].x;
        }
        else
        {
            a.ma=A[a.c[1]].ma;
            a.x=A[a.c[1]].x;
        }
    }
    void _insert(int x,int L,int R,int &k)
    {
        if(x>R||x<L)return ;
        if(!k)k=deep++;
        if(L==R)
        {
            A[k].ma=key;
            A[k].x=x;
            return ;
        }
        int mid=(L+R)>>1;
        _insert(x,L,mid,A[k].c[0]);
        _insert(x,mid+1,R,A[k].c[1]);
        update(A[k]);
    }
    void insert(int x,LL ke)
    {
        key=ke;
        _insert(x,0,n,root);
    }

    void _query(int l,int r,int L,int R,int k)
    {
        if(R<l||L>r)return ;
        if(L>=l&&R<=r)
        {
            if(A[k].ma>ma)
            {
                ma=A[k].ma;
                xx=A[k].x;
            }
            return ;
        }
        int mid=(L+R)>>1;
        _query(l,r,L,mid,A[k].c[0]);
        _query(l,r,mid+1,R,A[k].c[1]);
    }

    int query(int l,int r)
    {
        ma=-0x3f3f3f3f3f3f3f3f;
        _query(l,r,0,n,root);
        return xx;
    }
}T1,T2;
LL S1[MAXN*2];
LL S2[MAXN*2];
int main ()
{
    int n,m,s,l,r;
    LL h;
    scanf("%d %d",&n,&m);
    s=n<<1;
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",S1+i);
    }
    for(int i=n+1,t=1;i<s;i++,t++)  S1[i]=S1[t];
    for(int i=1;i<s;i++)    S1[i]+=S1[i-1];
    for(int i=0;i<s;i++)    S2[i]=S1[i];
    for(int i=0;i<n;i++)
    {
        scanf("%lld",&h);
        h=h<<1;
        S1[i]+=h;
        S2[i]-=h;
        S1[i+n]+=h;
        S2[i+n]-=h;
    }
    T1.n=s-1;
    T2.n=s-1;
    for(int i=0;i<s;i++)
    {
        T1.insert(i,S1[i]);
        T2.insert(i,-S2[i]);
    }
    while(m--)
    {
        scanf("%d %d",&l,&r);
        l--;
        r--;
        if(l<=r)
        {
            int u=r+1;
            r=n+l-1;
            l=u;
        }
        else
        {
            swap(l,r);
            l++;
            r--;
        }
        int ma=T1.query(l,r);
        int mi=T2.query(l,r);
        if(ma==mi)
        {
            int ma1=-1;
            if(ma>l)
                ma1=T1.query(l,ma-1);
            if(ma<r)
            {
                int u=T1.query(ma+1,r);
                if(ma1==-1||S1[ma1]<S1[u])ma1=u;
            }
            LL ans1=S1[ma1]-S2[mi];
            int mi1=-1;
            if(mi>l)
                mi1=T2.query(l,mi-1);
            if(mi<r)
            {
                int u=T2.query(mi+1,r);
                if(mi1==-1||(-S2[mi1])<(-S2[u]))mi1=u;
            }
            LL ans2=S1[ma]-S2[mi1];
            printf("%lld\n",max(ans1,ans2));
        }
        else
            printf("%lld\n",S1[ma]-S2[mi]);
    }
    return 0;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值