开车旅行

开车旅行

有n个东西向成一排的城市,自西向东编号为1~n,第i个城市的高度为\(h_i\),高度互不相同,定义两个城市i,j的距离\(dis(i,j)=|h_i-h_j|\)(大小的比较优先值,当值相同时,比较高度的大小),现在有两个人a,b在同一辆车从某个城市出发向东移动,并轮流开车,第一天a开车,a每天开车选择到达离它次近的城市,而b选择到离他最近的城市,现在有两种询问:

询问一:

给出x,询问从哪个城市出发,使行驶总距离不超过x,且a行驶的距离与b行驶的距离比值最大(分母为0算无限大,比值相同选高度最高的城市s)。

询问二:

有m组询问,每组询问有一个s,x,表示出发城市为s,求行驶总距离不超过x的情况下,a,b分别行驶的距离。

对于100%的数据,有\(1≤N≤100,000,1≤M≤100,000\)

其实最大的特点是发现一个确定的人在一个确定的位置,所能到达的下一个城市是固定的,而这是倍增的标志。

对于一个城市i,东边离它最近的城市自然是高度离它最近的,要维护则是把东边的城市和该城市排序后取这个城市附近的位置,而涉及要维护排序,自然是平衡树,因为还未学,所以set替代,注意先加入数在找其附近更好实现,次近同理。

于是设\(Ag[i]\)表示a在第i个城市所能到达的城市编号,同理有\(Bg[i]\),上诉已阐述维护办法,接着自然要求出\(go[i][j][k]\)表示在第i个城市,经过\(2^j\)天,人k(0A,1B)在头一天出发后到达的城市,不难有

\[go[i][0][0]=Ag[i],go[i][0][1]=Bg[i]\]

\[go[i][1][k]=go[go[i][0][k]][0][1-k]\]

\[go[i][j][k]=go[go[i][j-1][k]][j-1][k]\]

除了提到的,其他全部为0

接着我们要维护\(A[i][j][k],B[i][j][k]\)分别表示在第i个城市出发,经过\(2^j\)天,人k头天出发a行驶的距离,b行驶的距离,先全部初始化无限大

现在有

\[A[i][0][0]=dis(i,go[i][0][0]),A[i][0][1]=0\]

\[A[i][1][k]=A[i][0][k]+A[go[i][0][k]][0][1-k]\]

\[A[i][j][k]=A[i][j-1][k]+A[go[i][j-1][k]][j-1][k]\]

\[B[i][0][0]=0,B[i][0][1]=dis(i,go[i][0][1])\]

\[B[i][1][k]=B[i][0][k]+B[go[i][0][k]][0][1-k]\]

\[B[i][j][k]=B[i][j-1][k]+B[go[i][j-1][k]][j-1][k]\]

于是我们维护好了倍增数组,现在考虑如何回答询问,首先建立一个ask函数,给定s,x(同第二种询问变量意思),回答a,b分别行驶的距离,而对于不超过x,我们可以利用而二进制拆分,从大到小枚举接近答案,因此第二种询问也就解决了,时间复杂度\(log_2^n\)

而对于第一种询问,不难得知,我们可以支持枚举哪个城市,直接暴力算出比值,一个一个比即可。

因此问题得以解决,此题展示了倍增的交替使用,不妨叫做交替倍增,还考了常见的先维护位置倍增,再维护距离倍增,二进制拆分查询答案的方法,还有注意的事对stl的使用,如set。

参考代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <set>
#include <algorithm>
#define il inline
#define ri register
#define ll long long
#define swap(x,y) x^=y^=x^=y
#define Size 100000
#define intmax 0x7fffffff
using namespace std;
struct frac{
    int s,m;
    il void sort(){
        int d(gcd(s,m));
        if(d)s/=d,m/=d;
    }
    il int gcd(int a,int b){
        while(b)swap(a,b),b%=a;return a;
    }
    il bool operator==(frac&x){
        if(!x.m&&!m)return true;
        if(x.m==m&&s==x.s)return true;
        return false;
    }
    il bool operator<(frac&x){
        if(!m)return false;if(!x.m)return true;
        return (double)s/m<(double)x.s/x.m;
    }
}r1,r2;
struct pi{
    int x,y;
    il bool operator<(const pi&a)const{
        return x<a.x;
    }
}m,g[5];int gt;
set<pi>H;
set<pi>::iterator l,r;
ll A[Size+1][18][2],B[Size+1][18][2];
int Ag[Size+1],Bg[Size+1],go[Size+1][18][2],h[Size+1];
il pi ask(int,int);
il void prepare(int);
il bool comp(const pi&,const pi&);
template<class free>il free Abs(free);
template<class free>il void read(free&);
int main(){
    int n,s,x;
    read(n),prepare(n),read(x);
    for(int i(1);i<=n;++i){
        m=ask(i,x);
        r1=(frac){m.x,m.y},r1.sort();
        if(r1<r2)r2=r1,s=i;
        else if(r1==r2&&h[s]<h[i])s=i;
    }printf("%d\n",s),read(n);
    while(n--)
        read(s),read(x),m=ask(s,x),
            printf("%d %d\n",m.x,m.y);
    return 0;
}
il void prepare(int n){
    for(int i(1);i<=n;++i)read(h[i]);
    for(int i(n),j;i;--i){
        j=h[i],m=(pi){j,i},gt&=0;
        H.insert(m),l=r=H.find(m);
        if(l!=H.begin()){
            --l,g[++gt]=*l;
            if(l!=H.begin())--l,g[++gt]=*l;
        }
        if(++r,r!=H.end()){
            g[++gt]=*r;
            if(++r,r!=H.end())g[++gt]=*r;
        }sort(g+1,g+gt+1,comp);
        if(gt>=2)Ag[i]=g[2].y;
        if(gt>=1)Bg[i]=g[1].y;
    }h[0]=intmax;
    for(int i(n),j;i;--i){
        go[i][0][0]=Ag[i],go[i][0][1]=Bg[i];
        for(j=0;j<2;++j)go[i][1][j]=go[go[i][0][j]][0][1-j];
        for(j=2;j<18;++j)
            go[i][j][0]=go[go[i][j-1][0]][j-1][0],
                go[i][j][1]=go[go[i][j-1][1]][j-1][1];
    }memset(A,1,sizeof(A)),memset(B,1,sizeof(B));
    for(int i(n),j,k;i;--i){
        A[i][0][0]=Abs(h[go[i][0][0]]-h[i]),A[i][0][1]=0;
        B[i][0][0]=0,B[i][0][1]=Abs(h[go[i][0][1]]-h[i]);
        for(j=0;j<2;++j){
            A[i][1][j]=A[i][0][j]+A[go[i][0][j]][0][1-j];
            B[i][1][j]=B[i][0][j]+B[go[i][0][j]][0][1-j];
        }
        for(j=2;j<18;++j)
            for(k=0;k<2;++k){
                A[i][j][k]=A[i][j-1][k]+A[go[i][j-1][k]][j-1][k];
                B[i][j][k]=B[i][j-1][k]+B[go[i][j-1][k]][j-1][k];
            }
    }
}
il pi ask(int p,int x){
    int la(0),lb(0);
    for(int i(17);i>=0;--i)
        if(x>=A[p][i][0]+B[p][i][0])
            la+=A[p][i][0],lb+=B[p][i][0],
                x-=A[p][i][0]+B[p][i][0],p=go[p][i][0];
    return (pi){la,lb};
}
il bool comp(const pi&a,const pi&b){
    return Abs(a.x-m.x)==Abs(b.x-m.x)?
        a.x<b.x:Abs(a.x-m.x)<Abs(b.x-m.x);
}
template<class free>
il free Abs(free x){
    return x<0?-x:x;
}
template<class free>
il void read(free&x){
    x&=0;ri char c;while(c=getchar(),c==' '||c=='\r'||c=='\n');
    ri bool check(false);if(c=='-')check|=true,c=getchar();
    while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
    if(check)x=-x;
}

转载于:https://www.cnblogs.com/a1b3c7d9/p/10948935.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值