• 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
    ",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
    ",ta,tb);
        }
    }
  • 相关阅读:
    [NOI2012] 美食节
    [NOI2008] 志愿者招募
    P3834 【模板】可持久化线段树 2(主席树)
    P3919 【模板】可持久化线段树 1(可持久化数组)
    P4168 [Violet]蒲公英
    轻重链剖分
    沉舟侧畔千帆过 病树前头万木春
    P2119 魔法阵 (0.1s 虐杀过程)
    两行虐杀儒略历
    CSP2020 S-2 爆零(日)记 (已完结)
  • 原文地址:https://www.cnblogs.com/huangchenyan/p/11208235.html
Copyright © 2020-2023  润新知