洛谷1081/codevs1199 开车旅行 链表,倍增,模拟

题目分析

这题70分暴力很easy…正解有点难想…但是比较容易理解…可是代码比较难打…

work1

我们可以预处理从每一个城市出发小A和小B分别到的下一城市。
怎么处理?排序后用双向链表即可。很显然,若当前城市为x,那么排序后,小A和小B分别到的城市只用在x+1,x+2,x-1,x-2中找即可。每次搞事完毕后从链表中删除当前节点即可保证找的下一城市一定在当前城市后面。

int n,m;
int pos[N],f[N][21],fir[N],se[N];
LL kh[N],aa[N][21],bb[N][21];
struct node{int id,pre,nxt;LL h;}a[N];
bool cmp(node x,node y){return x.h<y.h;}
void gs(int x,int y){
    LL kl=abs(kh[x]-a[y].h),f1=abs(kh[fir[x]]-kh[x]),s1=abs(kh[se[x]]-kh[x]);
    if(!fir[x]||kl<f1)se[x]=fir[x],fir[x]=a[y].id;
    else if(!se[x]||kl<s1)se[x]=a[y].id;
}
void work1(){
    int i,x;
    for(i=1;i<=n;++i) a[i].h=read(),a[i].id=i,kh[i]=a[i].h;
    sort(a+1,a+1+n,cmp);
    for(i=1;i<=n;++i)pos[a[i].id]=i;
    for(i=1;i<=n;++i){
        if(i!=1)a[i].pre=i-1;
        if(i!=n)a[i].nxt=i+1;
    }
    for(i=1;i<=n;++i){
        x=pos[i];
        if(a[a[x].pre].pre)gs(i,a[a[x].pre].pre);
        if(a[x].pre)gs(i,a[x].pre);
        if(a[x].nxt)gs(i,a[x].nxt);
        if(a[a[x].nxt].nxt)gs(i,a[a[x].nxt].nxt);
        if(a[x].nxt)a[a[x].nxt].pre=a[x].pre;
        if(a[x].pre)a[a[x].pre].nxt=a[x].nxt;
    }
}

work2

直接模拟会TLE,想到log优化,就用倍增。
我们令“一轮”的定义为小A和小B各开一次车。
f(i,j)表示在i节点上走 2j 轮到达的位置,aa(i,j)走 2j 轮小A开的路程,bb(i,j)表示小B开的路程。
然后把这些数组预处理好…..

void work2(){
    int i,j;
    for(i=1;i<=n;++i){
        f[i][0]=fir[se[i]];
        if(se[i])aa[i][0]=abs(kh[se[i]]-kh[i]);
        if(f[i][0])bb[i][0]=abs(kh[f[i][0]]-kh[se[i]]);
    }
    for(j=1;j<=20;++j)
        for(i=1;i<=n;++i){
        f[i][j]=f[f[i][j-1]][j-1];
        aa[i][j]=aa[i][j-1]+aa[f[i][j-1]][j-1];
        bb[i][j]=bb[i][j-1]+bb[f[i][j-1]][j-1];
    }
}

work3

然后求答案啦。第一问枚举出发城市,第二问直接模拟即可。使用倍增思想完成。注意走了若干轮后,还要看小A能不能多走一次。

void getjs(int &x,LL &jsa,LL &jsb,LL &len){
    for(int i=20;i>=0;--i)
        if(f[x][i]&&aa[x][i]+bb[x][i]<=len){
        len-=aa[x][i]+bb[x][i];
        jsa+=aa[x][i],jsb+=bb[x][i],x=f[x][i];
    }
    if(se[x]&&abs(kh[se[x]]-kh[x])<=len)jsa+=abs(kh[se[x]]-kh[x]);
}
void work3(){//询问1
    db bz=LLONG_MAX;
    int i,x,j,ans;LL len,que,jsa,jsb;
    que=read();
    for(i=1;i<=n;++i){
        len=que,x=i,jsa=jsb=0,getjs(x,jsa,jsb,len);
        db kl;
        if(!jsb)kl=LLONG_MAX;
        else kl=jsa*1.0/jsb*1.0;
        if(kl<bz)bz=kl,ans=i;
        else if(fabs(kl-bz)<1e-10&&kh[ans]<kh[i])ans=i;
    }
    printf("%d\n",ans);
}
void work4(){//询问2
    int m,i,x;LL len,jsa,jsb;
    m=read();
    while(m--){
        x=read(),len=read(),jsa=jsb=0,getjs(x,jsa,jsb,len);
        printf("%lld %lld\n",jsa,jsb);
    }
}

题外话

代码模块化很重要。我打代码时调试work1调试了40分钟,其他模块都没有调很久。所以我就对错误位置有准确定位。否则这题可以调试到死……

总代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<climits>
#include<cmath>
using namespace std;
#define LL long long
#define db double
LL read(){
    LL q=0,w=1;char ch=' ';
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')w=-1,ch=getchar();
    while(ch>='0'&&ch<='9')q=q*10+(LL)(ch-'0'),ch=getchar();
    return q*w;
}
const int N=100005;
int n,m;
int pos[N],f[N][21],fir[N],se[N];
LL kh[N],aa[N][21],bb[N][21];
struct node{int id,pre,nxt;LL h;}a[N];
bool cmp(node x,node y){return x.h<y.h;}
void gs(int x,int y){
    LL kl=abs(kh[x]-a[y].h),f1=abs(kh[fir[x]]-kh[x]),s1=abs(kh[se[x]]-kh[x]);
    if(!fir[x]||kl<f1)se[x]=fir[x],fir[x]=a[y].id;
    else if(!se[x]||kl<s1)se[x]=a[y].id;
}
void work1(){//链表预处理下一城市
    int i,x;
    for(i=1;i<=n;++i) a[i].h=read(),a[i].id=i,kh[i]=a[i].h;
    sort(a+1,a+1+n,cmp);
    for(i=1;i<=n;++i)pos[a[i].id]=i;
    for(i=1;i<=n;++i){
        if(i!=1)a[i].pre=i-1;
        if(i!=n)a[i].nxt=i+1;
    }
    for(i=1;i<=n;++i){
        x=pos[i];
        if(a[a[x].pre].pre)gs(i,a[a[x].pre].pre);
        if(a[x].pre)gs(i,a[x].pre);
        if(a[x].nxt)gs(i,a[x].nxt);
        if(a[a[x].nxt].nxt)gs(i,a[a[x].nxt].nxt);
        if(a[x].nxt)a[a[x].nxt].pre=a[x].pre;
        if(a[x].pre)a[a[x].pre].nxt=a[x].nxt;
    }
}
void work2(){//预处理f,aa,bb
    int i,j;
    for(i=1;i<=n;++i){
        f[i][0]=fir[se[i]];
        if(se[i])aa[i][0]=abs(kh[se[i]]-kh[i]);
        if(f[i][0])bb[i][0]=abs(kh[f[i][0]]-kh[se[i]]);
    }
    for(j=1;j<=20;++j)
        for(i=1;i<=n;++i){
        f[i][j]=f[f[i][j-1]][j-1];
        aa[i][j]=aa[i][j-1]+aa[f[i][j-1]][j-1];
        bb[i][j]=bb[i][j-1]+bb[f[i][j-1]][j-1];
    }
}
void getjs(int &x,LL &jsa,LL &jsb,LL &len){
    for(int i=20;i>=0;--i)
        if(f[x][i]&&aa[x][i]+bb[x][i]<=len){
        len-=aa[x][i]+bb[x][i];
        jsa+=aa[x][i],jsb+=bb[x][i],x=f[x][i];
    }
    if(se[x]&&abs(kh[se[x]]-kh[x])<=len)jsa+=abs(kh[se[x]]-kh[x]);
}
void work3(){//询问1
    db bz=LLONG_MAX;
    int i,x,j,ans;LL len,que,jsa,jsb;
    que=read();
    for(i=1;i<=n;++i){
        len=que,x=i,jsa=jsb=0,getjs(x,jsa,jsb,len);
        db kl;
        if(!jsb)kl=LLONG_MAX;
        else kl=jsa*1.0/jsb*1.0;
        if(kl<bz)bz=kl,ans=i;
        else if(fabs(kl-bz)<1e-10&&kh[ans]<kh[i])ans=i;
    }
    printf("%d\n",ans);
}
void work4(){//询问2
    int m,i,x;LL len,jsa,jsb;
    m=read();
    while(m--){
        x=read(),len=read(),jsa=jsb=0,getjs(x,jsa,jsb,len);
        printf("%lld %lld\n",jsa,jsb);
    }
}
int main(){n=read();work1(),work2(),work3(),work4();return 0;}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值