Codeforces Round #292 (Div. 2) E. Drazil and Park (RMQ)

题目链接

题面:
在这里插入图片描述

题意:
给定一个圆,圆上有n个点,每个点有个属性 hi,第 i 个点与第 i + 1个点距离为 di
定义两个点之间的距离为 2*(hx + hy) + dis (x,y)。
从 x 到 y 可以顺时针走也可以逆时针走,中途会途径其他点(位于所走一侧的x与y之间的点会被途径)
现在给定某一个区间 [ l,r ] ,这个区间内的点均不能走,问在能选的区间内,我选择两个点,这两个点之间的最大距离是多少。

保证能选的点至少有两个。

题解:
在这里插入图片描述
由于所给定区间 [ l,r ] 中的点都不能走,如果 l,r区间一旦选定, 那么在能走的区间里面所选的两点 x,y,从x走到y的方式就已经确定了,要么顺时针,要么逆时针。

我们将环复制一倍变成链。
1 2 3 -----n 1 2 3 ----n
那么 x – y 之间的距离为 d [ x ] + d [ x+1 ] +…+ d [ y-1 ] + 2 * ( h [ x ] + h [ y ])

我们写作:
d [ 1 ] + d [ 2 ] +…+ d [ y-1 ] + 2 * h [ y ] + (2 * h [ x ] - (d [ 1 ] + d [ 2 ] +…+ d [ x - 1]))

我们令
d [ 1 ] + d [ 2 ] +…+ d [ k-1 ] + 2 * h [ k ] = Lk

2 * h [ k ] - (d [ 1 ] + d [ 2 ] +…+ d [ k - 1]) = Rk

我们设可选区间为 [ a ,b ],那么我们在 区间[ a ,b ] 中选出最大的 Lx 与 最大的 Ry,相加即是答案。
特别的,考虑 x==y时,由于要选两个不同的点,所以要得到次大值Lxx,与次大值Ryy,取max(Lx+Ryy,Lxx+Ry)。

所以我们用RMQ 预处理出区间最大值和区间次大值。

考虑不能选的区间 [ l , r ]
若 l <= r 那么可选区间为 r + 1 -------- l + n - 1
若 l >r 那么可选区间为 r + 1 ------- l - 1

代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<string>
#include<queue>
#include<bitset>
#include<map>
#include<set>
#define ll long long
#define llu unsigned ll
#define ld long double
#define ui unsigned int
#define pr make_pair
#define pb push_back
#define ui unsigned int
#define lc (cnt<<1)
#define rc (cnt<<1|1)
#define len(x)  (t[(x)].r-t[(x)].l+1)
#define tmid ((l+r)>>1)
#define forhead(x) for(int i=head[(x)];i;i=nt[i])
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)>(y)?(y):(x))
using namespace std;
const int inf=0x3f3f3f3f;
const ll lnf=0x3f3f3f3f3f3f3f3f;
const double dnf=1e18;
const int mod=1e9+7;
const double eps=1e-8;
const double pi=acos(-1.0);
const int maxm=100100;
const int up=100000;
const int hashp=13331;
const int maxn=200100;

int n,m,a,b,lo[maxn];
ll d[maxn],h[maxn];
pair<ll,ll> l[maxn][20][2],r[maxn][20][2];

void get(pair<ll,ll> a[],pair<ll,ll> b[],pair<ll,ll> c[])
{
    //在ask的时候会有重复的区间
    if(b[0]==c[0]&&b[1]==c[1])
    {
        a[0]=b[0];
        a[1]=b[1];
        return ;
    }
    else if(b[0]==c[0])
    {
        a[0]=b[0];
        a[1]=max(b[1],c[1]);
        return ;
    }


    if(b[0]<=c[0])
    {
        a[0]=c[0];
        a[1]=max(b[0],c[1]);
    }
    else
    {
        a[0]=b[0];
        a[1]=max(b[1],c[0]);
    }
}

void rmq(void)
{
    lo[0]=-1;
    for(int i=1;i<=n*2;i++)
    {
        l[i][0][0]=pr(d[i-1]+2*h[i],i);
        l[i][0][1]=pr(-lnf,-1);
        r[i][0][0]=pr(-d[i-1]+2*h[i],i);
        r[i][0][1]=pr(-lnf,-1);
        lo[i]=(i&(i-1))==0?lo[i-1]+1:lo[i-1];
        d[i]+=d[i-1];
    }
    int t=lo[n*2]+1;
    for(int j=1;j<=t;j++)
    {
        for(int i=1;i<=2*n-(1<<j)+1;i++)
        {
            get(l[i][j],l[i][j-1],l[i+(1<<(j-1))][j-1]);
            get(r[i][j],r[i][j-1],r[i+(1<<(j-1))][j-1]);
        }
    }
}

ll ask(int cl,int cr)
{
    pair<ll,ll>nowl[2],nowr[2];
    int k=lo[cr-cl+1];
    get(nowl,l[cl][k],l[cr-(1<<k)+1][k]);
    get(nowr,r[cl][k],r[cr-(1<<k)+1][k]);
    if(nowl[0].second!=nowr[0].second) return nowl[0].first+nowr[0].first;
    else return max(nowl[0].first+nowr[1].first,nowl[1].first+nowr[0].first);
}

int main(void)
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%lld",&d[i]),d[i+n]=d[i];
    for(int i=1;i<=n;i++)
        scanf("%lld",&h[i]),h[i+n]=h[i];
    rmq();
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&a,&b);
        if(a<=b) printf("%lld\n",ask(b+1,a+n-1));
        else printf("%lld\n",ask(b+1,a-1));
    }
    return 0;
}



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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值