题目分析

这题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节点上走$2^j$轮到达的位置,aa(i,j)走$2^j$轮小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分钟,其他模块都没有调很久。所以我就对错误位置有准确定位。否则这题可以调试到死……
还有好久没有人在k-xzy里写题解了……

总代码

#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;}
分类: 所有

litble

苟...苟活者在淡红的血色中,会依稀看见微茫的希望

发表评论

电子邮件地址不会被公开。 必填项已用*标注

你是机器人吗? =。= *