hdu 5261蜀道难 · 单调队列

题解

10^5的数据,常规做法理所当然的T了QAQ

看了一下别的大佬的博客,解法是 单调队列

能用单调队列,说明存在单调的性质,
这里是距离的单调,单调递减,队列里能加进来的点,如果其作为起点一定是不可以比前一个点作为起点产生的权值大
emmmm也不知道可不可以这么说↑

假设新加入的点为i,队列最后一个点为ed,h[i] 一定小于等于 h[ed]+dis(i,ed),否则,统计最大值,i点作为起点一定比ed作为起点更优(高度又高,且更易存在半径长度的区间内),应该直接放弃ed点

接下来是大致做法:

先将圆拆成线段,复制前面的数据,再多补一倍的区间,记作 h[1~2*n],
选用双端队列维护长度最大为半径大小( n 2 \frac{n}{2} 2n)的区间,(因为涉及到取第一个点和放弃最后一个点,当然数组万能)

设当前遍历到第i个点作为终点,
从队列里依次拿出一个点作为起点,如果两个端点距离超过半径,就pop这个起点,(超过半径了,说明反方向绕路更短,不用担心漏掉,我们补了一倍的区间呢),
如果在半径范围内,更新这个区间内最远的距离,
然后将当前点作为潜在的起点,和前面的点比较一下,清理一下队列末端的点,再加入到队列当中


在这里插入图片描述


#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+10;
int n,m,r,id1,id2;
ll h[N],ans;

void update(int x,int y){
    ll tmp=h[x]+h[y]+(y-x)*r;
    x%=n/2,y%=n/2;//还原坐标
    if(x>y)swap(x,y);
    if(tmp>ans){
        ans=tmp;
        id1=x;id2=y;
    }else if(tmp==ans && (x<id1 || (x==id1 && y<id2 ))){
        id1=x;id2=y;
    }
}

void solve(){
    deque<int>q;//用双端队列维护长度为半径大小的区间
    q.push_back(0);
    for (int i = 1; i < n; ++i) {//遍历端点
        while(!q.empty() && i-q.front()>m) //如果以队列第一个点位起点 当前点为终点 区间长度超过m 放弃
            q.pop_front();
        update(q.front(),i);//更新答案
        while(!q.empty() && h[q.back()]+(i-q.back())*r < h[i]) q.pop_back();
        q.push_back(i);
    }
}

int main(){
    ios::sync_with_stdio(0);

    int T;
    cin>>T;
    for (int cs = 1; cs <= T; ++cs) {
        printf("Case #%d:\n",cs);

        cin>>n>>r;
        for (int i = 0; i < n; ++i) {
            cin>>h[i];
            h[i+n]=h[i];
        }
        m=n/2;//半径
        n+=n;
        ans=0;
        solve();//单调队列
        printf("%d %d\n", id1+1,id2+1);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值