洛谷P1081 开车旅行 倍增+双向链表

题目链接:https://www.luogu.com.cn/problem/P1081

此题难点在于预处理(难想到)和一些细节。如果我们暴力找最近和次近的点时间复杂度为O(n^2),肯定超时。所以我们要换个方法。这里用双向链表,我们将每个点放进链表,按海拔从低到高排序。然后按从西往东的顺序,一个一个拿出来,它的最近点和次近点,就是 i-2 i-1 i+1 i+2 里面的。更新完这个点到最近和次近距离之后把这个点从链表钟删去,这样能把复杂度优化到O(nlogn)。然后我们再用倍增,处理每个点。最后输出答案。
代码如下

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
const int inf=0xfffffff;
struct node
{
    ll height;//海拔
    int last,next;//前驱与后继
    int id;//排序前编号
}p[maxn];
int pos[maxn];//排序后的位置
ll dis1[maxn];//距离i最近的距离
ll dis2[maxn];//距离i次近的距离
int to1[maxn];//距离i最近的城市
int to2[maxn];//距离i次近的城市
bool cmp(node a,node b)
{
    return a.height < b.height;
}
int n,m;
void modify(int i,int loc,int x)
{
    if(x<1||x>n)
    return ;
    if(!dis1[i]||dis1[i]>abs(p[loc].height-p[x].height)||(dis1[i]==abs(p[loc].height-p[x].height)
            &&p[pos[to1[i]]].height>p[x].height))
    {
        dis2[i]=dis1[i];
        to2[i]=to1[i];
        dis1[i]=abs(p[loc].height-p[x].height);
        to1[i]=p[x].id;
    }
    else if(!dis2[i]||dis2[i]>abs(p[loc].height-p[x].height)||(dis2[i]==
            abs(p[loc].height-p[x].height)&&p[pos[to2[i]]].height>p[x].height))
    {
        dis2[i]=abs(p[loc].height-p[x].height);
        to2[i]=p[x].id;
    }
}
ll dis4[maxn][21];//i交换驾驶2^j次a走的距离
ll dis5[maxn][21];//i交换驾驶2^j次b走的距离
ll dis6[maxn][21];//i交换驾驶2^j次a与b一共走的距离
int to3[maxn][21];//i交换驾驶2^j次到达的城市
void init()//倍增初始化
{
    for(int i=1;i<=n;i++)
    {
        dis4[i][0]=dis2[i];
        dis5[i][0]=dis1[to2[i]];
        dis6[i][0]=dis2[i]+dis1[to2[i]];
        to3[i][0]=to1[to2[i]];
    }
}
void Double()//倍增
{
    for(int i=1;i<=20;i++)
    for(int j=1;j<=n;j++)
    {
        to3[j][i]=to3[to3[j][i-1]][i-1];
        if(to3[j][i])
        {
            dis4[j][i]=dis4[j][i-1]+dis4[to3[j][i-1]][i-1];
            dis5[j][i]=dis5[j][i-1]+dis5[to3[j][i-1]][i-1];
            dis6[j][i]=dis6[j][i-1]+dis6[to3[j][i-1]][i-1];
        }
    }
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&p[i].height);
        p[i].id=i;
    }
    sort(p+1,p+1+n,cmp);
    for(int i=1;i<=n;i++)
    {
        if(i!=1)
        p[i].last=i-1;
        if(i!=n)
        p[i].next=i+1;
    }
    for(int i=1;i<=n;i++)
    pos[p[i].id]=i;
    for(int i=1;i<=n;i++)
    {
        int t=pos[i];
        modify(i,t,p[p[t].next].next);
        modify(i,t,p[p[t].last].last);
        modify(i,t,p[t].next);
        modify(i,t,p[t].last);
        if(p[t].next)
        p[p[t].next].last=p[t].last;
        if(p[t].last)
        p[p[t].last].next=p[t].next;
        p[t].last=0;
        p[t].next=0;
    }
    init();
    Double();
    ll x0,x;
    int s;
    scanf("%lld",&x0);
    double Mi=inf;
    int t=0;
    for(int i=1;i<=n;i++)
    {
        ll a=0,b=0,tx;
        tx=x0;
        int y=i;
        for(int j=20;j>=0;j--)
        {
            if(dis6[y][j]&&dis6[y][j]<=tx)
            {
                tx-=dis6[y][j];
                a+=dis4[y][j];
                b+=dis5[y][j];
                y=to3[y][j];
            }
        }
        if(dis2[y]<=tx)
        a+=dis2[y];
        if(a<=0)
        continue;
        if(!t||(double)a/(double)b-Mi<-0.00000001
           ||(fabs((double)a/(double)b-Mi)<=0.00000001&&p[pos[t]].height<p[pos[i]].height))
           {
               Mi=(double)a/(double)b;
               t=i;
           }
    }
    printf("%d\n",t);
    scanf("%d",&m);
    while(m--)
    {
        ll a=0,b=0;
        scanf("%d %lld",&s,&x);
        for(int i=20;i>=0;i--)
        {
            if(dis6[s][i]&&dis6[s][i]<=x)
            {
                x-=dis6[s][i];
                a+=dis4[s][i];
                b+=dis5[s][i];
                s=to3[s][i];
            }
        }
        if(dis2[s]<=x)
        a+=dis2[s];
        printf("%lld %lld\n",a,b);
    }
    //for(int i=0;i<=20;i++)
    //for(int j=1;j<=n;j++)
    //printf("%lld\n",dis4[j][i]);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_44491423/article/details/104546894