NOIP 2012 P1081 开车旅行

倍增

这道题最难的应该是预处理。。。

首先用set从后往前预处理出每一个点海拔差绝对值得最大值和次大值

因为当前城市的下标只能变大,对于点i,在set中二分找出与其值最接近的下标

然后再set中将左右各两个下标处理出来,取最大值和次小值

预处理完毕

将每一次小A和小B的开车看为一轮开车

然后用g[i][j]表示从第i个点出发经过了2^j轮开车到达的点

la[i][j]表示从第i个点出发经过了2^j轮开车小A开的路程

lb[i][j]表示从第i个点出发经过了2^j轮开车小B开的路程

然后就是倍增处理

还有要注意,当完整的一轮无法在进行后,小A还有可能再开一轮车,进行判断即可

#include <bits/stdc++.h>
#define ll long long
#define inf 1e9
using namespace std;
const ll MAXN=100100;
ll n,x0,a[MAXN],b[MAXN],g[MAXN][32];
ll h[MAXN],where,ding,m;
ll la[MAXN][32],lb[MAXN][32];
double MIN;
set <pair<ll,ll> > s;
bool cmp(pair<ll,ll> a,pair<ll,ll> b)
{
    return (abs(a.first-ding)<abs(b.first-ding) || (abs(a.first-ding)==abs(b.first-ding) && h[a.second]<h[b.second]));
}
int main()
{
    scanf("%lld",&n);
    for (ll i=1;i<=n;i++)
      scanf("%lld",&h[i]);
    a[n]=b[n]=-1;//-1表示无法再进行开车
    s.insert(make_pair(h[n],n));
    b[n-1]=n;a[n-1]=-1;
    s.insert(make_pair(h[n-1],n-1));
    for (ll i=n-2;i>=1;i--)//预处理
    {
        bool bl=0;
        set <pair<ll,ll> > :: iterator it,be;
        vector <pair<ll,ll> > k;
        k.clear();
        it=s.lower_bound(make_pair(h[i],i));
        be=s.begin();
        be--;
        if (it!=s.end())
        {
            k.push_back(*it);
            it++;
            bl=1;
        }
        if (it!=s.end())
          k.push_back(*it);
        if (bl)
          it--;
        it--;
        if (it!=be)
        {
            k.push_back(*it);
            it--;
        }
        if (it!=be)
          k.push_back(*it);//处理出左右4个点
        ding=h[i];
        sort(k.begin(),k.end(),cmp);
        b[i]=k[0].second;//取最大
        a[i]=k[1].second;//取次大
        s.insert(make_pair(h[i],i));
    }
    for (ll i=1;i<=n;i++)
    {
        g[i][0]=-1;
        if (a[i]!=-1 && b[a[i]]!=-1)
        {
            la[i][0]=abs(h[i]-h[a[i]]);
            lb[i][0]=abs(h[a[i]]-h[b[a[i]]]);
            g[i][0]=b[a[i]];//倍增预处理
        }
    }
    for (ll i=1;i<=30;i++)
    {
        for (ll j=1;j<=n;j++)
        {
            if (g[j][i-1]!=-1 && g[g[j][i-1]][i-1]!=-1)//保证不会超出范围
            {
                g[j][i]=g[g[j][i-1]][i-1];
                la[j][i]=la[j][i-1]+la[g[j][i-1]][i-1];
                lb[j][i]=lb[j][i-1]+lb[g[j][i-1]][i-1];//进行倍增
            }
            else
              g[j][i]=-1;
        }
    }
    scanf("%lld",&x0);
    MIN=1.0e18+10;
    where=0;
    for (ll i=1;i<=n;i++)
    {
        ll ta,tb,now;
        ta=tb=0;
        now=i;
        for (ll j=30;j>=0;j--)//搜索
        {
            if (g[now][j]!=-1 && ta+tb+la[now][j]+lb[now][j]<=x0)
            {
                ta+=la[now][j];
                tb+=lb[now][j];
                now=g[now][j];
            }
        }
        if (a[now]!=-1 && ta+tb+abs(h[now]-h[a[now]])<=x0)
        {
            ta+=abs(h[now]-h[a[now]]);
            now=a[now];
        }
        double temp;
        if (tb==0)
          temp=1.0e18;
        else
          temp=(double)(1.0*ta)/(1.0*tb);
        if (temp<MIN)
        {
            MIN=temp;
            where=i;
        }
        else
        if (temp==MIN)
        {
            if (h[i]>h[where])
              where=i;
        }
    }
    printf("%lld\n",where);
    scanf("%lld",&m);
    for (ll i=1;i<=m;i++)
    {
        ll now,x;
        scanf("%lld%lld",&now,&x);
        ll ta,tb;
        ta=tb=0;
        for (ll j=30;j>=0;j--)
        {
            if (g[now][j]!=-1 && ta+tb+la[now][j]+lb[now][j]<=x)
            {
                ta+=la[now][j];
                tb+=lb[now][j];
                now=g[now][j];
            }
        }
        if (a[now]!=-1 && ta+tb+abs(h[now]-h[a[now]])<=x)
        {
            ta+=abs(h[now]-h[a[now]]);
            now=a[now];
        }
        printf("%lld %lld\n",ta,tb);
    }
}

 

转载于:https://www.cnblogs.com/huangchenyan/p/11208235.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值