$ P1081 \ [NOIP2012] $ traveling by car


This question is a little thing, is demodulated by nearly \ (2 \) hours to untangle,NOIP\ (CSP \) to test this question certainly \ (GG \) a

\(Description\)

Surface title
Title effect is \ (n-\) cities, location is \ (. 1-n-\) , each city there is a distance between the two cities each constant altitude, defined as altitude difference absolute value \ ( a, B \) take turns driving, \ (a \) to open (awkward saying this is good, why have to let time near the first open), only open to the right, \ (a \) strategy is to go to the next nearest city, \ (B \) strategy is to go to the nearest city. The first key is the nearest city to determine the absolute value of the difference between the minimum altitude, the lower the altitude if the same is closer.
There are two problems:
The first question is for you driving distance \ (x_0 \) , requires you to find a starting point \ (s_0 \) so can not get open position \ (\ frac {dis2} { dis1} \) maximum (If \ (dis1 = 0 \) is considered infinite), output \ (S_0 \) , can not open position refers to a small value or can not find the time or less than the minimum distance.
The second problem is to give you \ (m \) start position and distance \ (S_I, x_i \) , the required output under given conditions \ (A, B \) respectively as the distance.

\(Solution\)

This question is in \ (qbxt \) have said, will not only know how to do is doubled, then the first to write violence.
For \ (sub1 \) case, the need to enumerate all positions, and then go backwards step by step, every step backwards, scanning to find the next smallest minimum value, obviously \ (O (the n-^ 3) \) , found there \ (40pts \) violence points.
Considering the minimum time to find each minimum value of the back of this process is too cumbersome, and the same position a plurality of times will be looking for, then put out \ (O (n ^ 2) \) preprocessing each position behind minimum the position of the next smallest value, \ (sub1 \) directly enumerate all positions one by one to jump on the line, \ (sub2 \) is also simultaneously optimized.
Complexity of the whole process down \ (O (mn) \) or \ (O (n ^ 2) \) or so, discovered by \ (70pts! \)
Quickly pay up discovery \ (65pts \) , and then found forgot to turn \ (long \ long \) there \ (double \) the maximum value set is not enough, the examination must pay attention to ah!

\(70pts\ Code\)

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#define re register
#define maxn 200100
#define MIN 2147483647
#define ll long long
using namespace std;
inline int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int s[maxn],x[maxn],x0,s0,n,m,tmp1,tmp2,tmp3,tmp4,pos1,pos2,Ans;
int nxt1[maxn],nxt2[maxn];
ll ans1[maxn],ans2[maxn],h[maxn];
double ans,dis1,dis2;
bool CMP(int a,int b,int c,int d)
{
    if(a<c) return true;
    else if(a==c&&b<d) return true;
    return false;
}
void pre()
{
    for(re int i=1;i<=n;++i)
    {
        tmp1=tmp2=MIN;
        pos1=pos2=0;
        tmp3=tmp4=MIN;
        for(re int j=i+1;j<=n;++j)
        {
            if(CMP(abs(h[i]-h[j]),h[j],tmp1,tmp3))
            {
                tmp2=tmp1,tmp4=tmp3,pos2=pos1;
                tmp1=abs(h[i]-h[j]),tmp3=h[j],pos1=j;
            }
            else if(CMP(abs(h[i]-h[j]),h[j],tmp2,tmp4))
            {
                tmp2=abs(h[i]-h[j]),tmp4=h[j],pos2=j;
            }
        }
        nxt1[i]=pos1,nxt2[i]=pos2;
    }
} 
void work1()
{
    ans=MIN;
    for(re int i=1;i<=n;++i)//枚举起点
    {
        dis1=dis2=0;
        int now=i;
        for(re int j=1;j<=n;++j)
        {
            if(j&1)//是奇数 找次小 
            {
                if(dis1+dis2+abs(h[nxt2[now]]-h[now])>x0) break;
                if(!nxt2[now]) break;
                dis2+=abs(h[nxt2[now]]-h[now]);
                now=nxt2[now];
            }
            else//找最小 
            {
                if(dis1+dis2+abs(h[nxt1[now]]-h[now])>x0) break;
                if(!nxt1[now]) break;
                dis1+=abs(h[nxt1[now]]-h[now]);
                now=nxt1[now]; 
            }
        }
        
        double t;
        if(dis1==0) t=MIN;
        else t=dis2/dis1*1.0;
        if(t<ans)
        {
            ans=t,s0=i;
        }
        
    } 
}
void work2()
{
    ll d1,d2;
    for(re int i=1;i<=m;++i)
    {
        int now=s[i];
        d1=d2=0;
        for(re int j=1;j<=n;++j)
        {
            if(j&1)//是奇数 找次小 
            {
                if(d1+d2+abs(h[nxt2[now]]-h[now])>x[i]) break;
                if(!nxt2[now]) break;
                d2+=(ll)abs(h[nxt2[now]]-h[now]);
                now=nxt2[now];
            }
            else//找最小 
            {
                if(d1+d2+abs(h[nxt1[now]]-h[now])>x[i]) break;
                if(!nxt1[now]) break;
                d1+=(ll)abs(h[nxt1[now]]-h[now]);
                now=nxt1[now]; 
            }
        }
        ans1[i]=d1,ans2[i]=d2;
    } 
}
int main()
{
    n=read();
    for(re int i=1;i<=n;++i) h[i]=read();
    x0=read();
    m=read();
    for(re int i=1;i<=m;++i)
        s[i]=read(),x[i]=read();
    pre();
    work1();
    printf("%d\n",s0);
    work2();
    for(re int i=1;i<=m;++i)
    printf("%lld %lld\n",ans2[i],ans1[i]);
    return 0;
}

If the test does not have a sub-violence certainly not to think about positive solutions

\(100pts\)

注意到两个瓶颈在于初始化和跳的操作,看这个数据我们需要优化成\(O(nlogn)\)
所以很容易想到跳的操作可以使用倍增优化
那么什么时候可以使用倍增优化呢

当不断进行类似的过程且不存在最优解时可以使用
比如求和、求最值、求最终位置、求距离等等
在这个题中,当谁在开车一定时,每一个位置的后继是一定的,走的距离也是一定的,所以这个过程可以用倍增优化掉
设走的总步数是\(2^j\)不便于转移,因为会出现总步数是\(1\),还要判断是谁走的情况,所以设每个人分别走了\(2^j\)
代码还是比较简单的,注意分清每个数组的意义,别搞混了

void pre2()//倍增预处理 
{
    for(re int i=1;i<=n;++i)
    {
        //d2[i]表示i后的次小距离,d1[i]表示最小距离 
        //d4[i][j]表示从i开始,A,B分别开(1<<j)步A一个人的路程 
        //d5[i][j]表示从i开始,A,B分别开(1<<j)步B一个人的路程
        //d3[i][j]表示从i开始,A,B分别开(1<<j)步A,B总路程
        d4[i][0]=d2[i];
        d5[i][0]=d1[nxt2[i]];
        d3[i][0]=d4[i][0]+d5[i][0];
        nxt[i][0]=nxt1[nxt2[i]];
        //nxt1[i]表示B开车的下一个城市,nxt2[i]表示A开车的下一个城市
        //nxt[i][j]表示i开始,A,B分别开(1<<j)步,最终位置 
    }
    for(re int j=1;j<=20;++j)//注意从小到大放到外层枚举 
     for(re int i=1;i<=n;++i)
     {
        nxt[i][j]=nxt[nxt[i][j-1]][j-1];
        if(!nxt[i][j]) continue;//如果出去了就不用算距离了 
        d3[i][j]=d3[i][j-1]+d3[nxt[i][j-1]][j-1];
        d4[i][j]=d4[i][j-1]+d4[nxt[i][j-1]][j-1];
        d5[i][j]=d5[i][j-1]+d5[nxt[i][j-1]][j-1];
     }
}

使用倍增跳的过程是这样的:先让两个一起跳,即走\(d3[i][j]\),判断是否超距离,是否越界,最后如果还能走就只能让\(A\)开一次了,所以判断一下即可。
这样跳的过程复杂度就变成了\(O(nlogn)\),但预处理的过程仍然是\(O(n^2)\)的,所以必须优化这个过程
讲道理这道题倍增比预处理过程的优化要好像,要用到双向链表
一般要用到双向链表的地方是将一个数组位置快速删除且能正常遍历,且删除的目的是让前面扫描过元素的不再被考虑(很重要的性质)
比如这道题,我们预处理时可以先开结构体按海拔排序,记录他们原来的位置,再开个桶记录原来位置到现在结构体下标的映射。在原来数组从左向右扫描,设它在结构体中的位置是\(pos[i]\),它的次近和最近海拔一定是在\(pos[i]-2,pos[i]-1,pos[i]+1pos[i]+2\)(因为极端情况是两个都在一边)。然后在这四个位置上找最小值次小值就行(注意判断越界的情况),然后把\(pos[i]\)删去,这样才能保证下一次寻找时不会考虑到原来数组中在它左边元素,这些操作用双向链表可以很好完成。
所以这道题就\(AC\)了,总的来说,暴力分很足,难点在于双向链表和倍增

\(100pts\ Code\)

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#define re register
#define maxn 100100
#define MIN 2147483647
#define ll long long
using namespace std;
inline int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
struct Cup{
    ll h;
    int l,r,pos;
}City[maxn]; 
bool cmp(Cup A,Cup B)
{
    return A.h<B.h;
}
int s[maxn],x[maxn],x0,s0,n,m,tmp1,tmp2,tmp3,tmp4,pos1,pos2;
int nxt1[maxn],nxt2[maxn],post[maxn],c[120],nxt[maxn][23];
ll ans1[maxn],ans2[maxn],d1[maxn],d2[maxn],d3[maxn][23],d4[maxn][23],d5[maxn][23];
double ans=MIN,dis1,dis2;
bool CMP(ll a,ll b,ll c,ll d)//按关键字比较
{
    if(a<c) return true;
    else if(a==c&&b<d) return true;
    return false;
}
void del(int id)//双向链表 
{
    City[City[id].l].r=City[id].r;
    City[City[id].r].l=City[id].l;
}
void pre()
{
    for(re int i=1;i<=n;++i)
    {
        tmp1=tmp2=tmp3=tmp4=MIN;
        pos1=pos2=0;
        int tmp=post[i];//表示他在结构体中的位置
        c[1]=City[tmp].l,c[2]=City[City[tmp].l].l;
        c[3]=City[tmp].r,c[4]=City[City[tmp].r].r;
        for(re int j=1;j<=4;++j)
        {
            int now=c[j];
            if(now==0||now==n+1) continue;//这里注意要判断全了
            if(CMP(abs(City[tmp].h-City[now].h),City[now].h,tmp1,tmp3))//找最小值次小值,tmp1是最小距离,tmp3是最小距离的海拔,tmp2,tmp4是次小
            {
                tmp2=tmp1,tmp4=tmp3,pos2=pos1;
                tmp1=abs(City[tmp].h-City[now].h);
                tmp3=City[now].h;
                pos1=now;
            }
            else if(CMP(abs(City[tmp].h-City[now].h),City[now].h,tmp2,tmp4))
            {
                tmp2=abs(City[tmp].h-City[now].h);
                tmp4=City[now].h;
                pos2=now;
            }
        }
        
        nxt1[i]=City[pos1].pos,nxt2[i]=City[pos2].pos;//注意这里的i,City[pos1].pos都是原数组位置
        d1[i]=tmp1,d2[i]=tmp2;
        del(tmp);//删除
    }
} 
void pre2()//倍增预处理 
{
    for(re int i=1;i<=n;++i)
    {
        //d2[i]表示i后的次小距离,d1[i]表示最小距离 
        //d4[i][j]表示从i开始,A,B分别开(1<<j)步A一个人的路程 
        //d5[i][j]表示从i开始,A,B分别开(1<<j)步B一个人的路程
        //d3[i][j]表示从i开始,A,B分别开(1<<j)步A,B总路程
        d4[i][0]=d2[i];
        d5[i][0]=d1[nxt2[i]];
        d3[i][0]=d4[i][0]+d5[i][0];
        nxt[i][0]=nxt1[nxt2[i]];
        //nxt1[i]表示B开车的下一个城市,nxt2[i]表示A开车的下一个城市
        //nxt[i][j]表示i开始,A,B分别开(1<<j)步,最终位置 
    }
    for(re int j=1;j<=20;++j)//注意从小到大放到外层枚举 
     for(re int i=1;i<=n;++i)
     {
        nxt[i][j]=nxt[nxt[i][j-1]][j-1];
        if(!nxt[i][j]) continue;//如果出去了就不用算距离了 
        d3[i][j]=d3[i][j-1]+d3[nxt[i][j-1]][j-1];
        d4[i][j]=d4[i][j-1]+d4[nxt[i][j-1]][j-1];
        d5[i][j]=d5[i][j-1]+d5[nxt[i][j-1]][j-1];
     }
} 
void work1()//找点
{
    int tmp=x0;
    for(re int i=1;i<=n;++i)
    {
        dis1=dis2=0;
        int now=i;
        x0=tmp;
        for(re int j=20;j>=0;--j)//按上面的方法走就行
        {
            if(nxt[now][j]==0||x0-d3[now][j]<0) continue;
            x0-=d3[now][j],dis1+=d5[now][j],dis2+=d4[now][j];
            now=nxt[now][j];
        }
        if(nxt2[now]!=0&&x0-d2[now]>=0) dis2+=d2[now];//小A还能再走 
        double t;
        if(dis1<=1e-7) t=MIN;//不要写if(dis==0),因为有精度问题,还有t要赋值足够大
        else t=1.0*dis2/dis1;
        if(t<ans) ans=t,s0=i;
    }
} 
void work2()
{
    for(re int i=1;i<=m;++i)
    {
        int now=s[i];
        for(re int j=20;j>=0;--j)
        {
            if(nxt[now][j]==0||x[i]-d3[now][j]<0) continue;
            ans1[i]+=d5[now][j],ans2[i]+=d4[now][j],x[i]-=d3[now][j];
            now=nxt[now][j];
        }
        if(nxt2[now]!=0&&x[i]-d2[now]>=0) ans2[i]+=d2[now];
    }
}
int main()
{
    n=read();
    for(re int i=1;i<=n;++i)
    {
        City[i].h=read();
        City[i].pos=i;//它在结构体中的位置->原位置
    }
    sort(City+1,City+1+n,cmp);
    for(re int i=1;i<=n;++i)
    {
        post[City[i].pos]=i;//原位置->它在结构体中的位置
        City[i].l=i-1;
        City[i].r=i+1;
    }
    pre();
    pre2();
    x0=read();
    m=read();
    for(re int i=1;i<=m;++i)
        s[i]=read(),x[i]=read();
    work1();
    printf("%d\n",s0);
    work2();
    for(re int i=1;i<=m;++i)
    printf("%lld %lld\n",ans2[i],ans1[i]);
    return 0;
}

Guess you like

Origin www.cnblogs.com/Liuz8848/p/11794998.html