P1081 开车旅行 倍增 洛谷

题目连接

题意

题目已经说的hin明确了。

题解

我们要求出从每个点出发,小A要走的城市和小B要走的城市。

我们把 i i 以后的所有点的海拔加入到set,然后拿 H[i] H [ i ] 到set里面去lower_bound,找到比H[i]大的两个地点和比H[i]小的两个地点,并把这四个地点与H[i]的差值加入到新的排序数组中,排个序,找到差值最小的两个点,分别就是小B要选的目的地和小A要选的目的地。
在这里注意一点,就是当差值最小的两个值相等的时候,小B走的是海拔较低的那个点,涉及到第二关键字的问题,我们可以用一个比较省事的方法,那就是给海拔低的点计算差值时候乘以99999999,海拔较高的点计算差值时候乘以100000000。

定义需要的状态

nxt1[i] n x t 1 [ i ] 代表从 i i 出发,小B的目的地。
nxt2[i]代表从 i i 出发,小A的目的地。
nxt3[i][j]代表从 i i 出发,(小A走完、小B走完)2j轮,所达到的目的
地。

DP1[i][j] D P 1 [ i ] [ j ] 代表从i出发,(小A走完、小B走完) 2j 2 j 轮,小B所经过的距离。

DP2[i][j] D P 2 [ i ] [ j ] 代表从i出发,(小A走完、小B走完) 2j 2 j 轮,小A所经过的距离。

nxt3,DP1,DP2可以用类似于处理ST表的方法求出来。

函数解释

一轮代表小A走一次+小B走一次。
getdp(int &S,int len,ll &ans1,ll &ans2)
从S出发,走len轮,返回ans1为小B走过的路程,返回ans2为小A走过的路程,并且把S改变为len轮以后到达的点。
getmx(int S,ll X)
从S出发,总路程不能超过X,返回最大能走几轮。

回答询问

给出 Si S i Xi X i ,用二分的方法求出从Si出发小A和小B最长能走的长度。


代码

// luogu-judger-enable-o2
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <set>
using namespace std;
typedef long long ll;
typedef pair<ll,int> pll;
set<pll>::iterator it;
const int maxn = 500007;
int nxt1[maxn],nxt2[maxn],nxt3[maxn][30];
int N,LOG,X0,M;
ll H[maxn],H2[maxn],H1[maxn],DP2[maxn][30],DP1[maxn][30];
pll tmpsort[7];
void getdp(int &S,int len,ll &ans1,ll &ans2){
    int cnt = 0;
    while(len){
        if(len & 1){
            ans1 += DP1[S][cnt];
            ans2 += DP2[S][cnt];
            S = nxt3[S][cnt];
        }
        len >>= 1;
        cnt++;
        if(!S) break;
    }
    return ;
}
int getmx(int S,ll X){
    int l = 0,mid,r = N+1;
    while(r - l > 1){
        mid = (l+r)/2;
        int nS = S;
        ll ans1 = 0,ans2 = 0;
        getdp(nS,mid,ans1,ans2);
        if(ans1+ans2 > X || !nS)
            r = mid;
        else 
            l = mid;
    }
    return l;
}
int main(){
    set<pll> st;
    cin>>N;
    int tmp = 1;
    while(tmp <= N/3)
        LOG++,tmp <<= 1;
    for(int i = 1;i <= N;++i){
        scanf("%lld",&H[i]);
        st.insert(make_pair(H[i],i));
    }
    for(int i = 1;i < N;++i){
        int ct = 0;
        st.erase(make_pair(H[i],i));
        it = st.lower_bound(make_pair(H[i],i));
        if(it != st.end()){
            tmpsort[ct++] = make_pair(abs(H[i]-(*it).first)*100000000,(*it).second);    
            ++it;
        }
        if(it != st.end()){
            tmpsort[ct++] = make_pair(abs(H[i]-(*it).first)*100000000,(*it).second);    
        }  
        it = st.lower_bound(make_pair(H[i],i));

        if(it != st.begin()) {
            --it;
            tmpsort[ct++] = make_pair(abs(H[i]-(*it).first)*99999999,(*it).second);    
            if(it != st.begin()){
                --it;
                tmpsort[ct++] = make_pair(abs(H[i]-(*it).first)*99999999,(*it).second); 
            }
        }
        sort(tmpsort,tmpsort+ct);
        if(ct > 0)
            nxt1[i] = tmpsort[0].second;
        if(ct > 1)
            nxt2[i] = tmpsort[1].second;
    }
    for(int i = 1;i < N;++i)
        nxt3[i][0] = nxt1[nxt2[i]];
    for(int i =1;i <= N;++i){
        if(nxt1[i])
            H1[i] = abs(H[nxt1[i]] - H[i]);
        if(nxt2[i]){
            H2[i] = abs(H[nxt2[i]] - H[i]);
            DP2[i][0] = abs(H[nxt2[i]] - H[i]);
        }
        if(nxt1[nxt2[i]])
            DP1[i][0] = abs(H[nxt1[nxt2[i]]] - H[nxt2[i]]);
    }
    for(int j = 1;j <= LOG;++j){
        for(int i = 1;i <= N;++i){
            nxt3[i][j] = nxt3[nxt3[i][j-1]][j-1];
            DP1[i][j] = DP1[i][j-1] + DP1[nxt3[i][j-1]][j-1];
            DP2[i][j] = DP2[i][j-1] + DP2[nxt3[i][j-1]][j-1];
        }
    }
    int mf1 = 0,mf2 = 1,mh = 0;
    int ans1 = 1;
    cin>>X0>>M;
    for(int i = 1;i <= N;++i){
        ll f1 = 0,f2 = 0;
        int nS = i;
        int mxl = getmx(i,X0);
        getdp(nS,mxl,f1,f2);
        if(nxt2[nS] && H2[nS]+f1+f2 <= X0)
            f2 += H2[nS];
        if(!f2) continue;
        if(mf1*f2 < f1*mf2){
            mf1 = f1;
            mf2 = f2;
            ans1 = i;
            mh = H[i];
        }
        else if(mf1*f2 == mf2*f1 && H[i] > mh){
            mf1 = f1;
            mf2 = f2;
            ans1 = i;
            mh = H[i];
        }
    }
    cout<<ans1<<endl;
    for(int i = 0;i < M;++i){
        int Si,nSi;ll Xi;
        scanf("%d %lld",&Si,&Xi);
        nSi = Si;
        int mxl = getmx(Si,Xi);
        ll f1 = 0,f2 = 0;
        getdp(nSi,mxl,f1,f2);
        if(nxt2[nSi] && H2[nSi]+f1+f2 <= Xi)
            f2 += H2[nSi];
        printf("%lld %lld\n",f2,f1);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值