【NOIP2012提高组】开车旅行

Description

小 A 和小 B 决定利用假期外出旅行,他们将想去的城市从 1 到 N 编号,且编号较小的城市在编号较大的城市的西边,已知各个城市的海拔高度互不相同,记城市 i 的海拔高度为Hi ,城市 i 和城市 j 之间的距离 d[i,j]恰好是这两个城市海拔高度之差的绝对值,即d[i, j] = |H i − Hj |。
旅行过程中,小 A 和小 B 轮流开车,第一天小 A 开车,之后每天轮换一次。他们计划
选择一个城市 S 作为起点,一直向东行驶,并且最多行驶 X 公里就结束旅行。小 A 和小 B的驾驶风格不同,小 B 总是沿着前进方向选择一个最近的城市作为目的地,而小 A 总是沿着前进方向选择第二近的城市作为目的地(注意:本题中如果当前城市到两个城市的距离相同,则认为离海拔低的那个城市更近)。如果其中任何一人无法按照自己的原则选择目的城市,或者到达目的地会使行驶的总距离超出 X 公里,他们就会结束旅行。
在启程之前,小 A 想知道两个问题:

  1. 对于一个给定的 X=X0,从哪一个城市出发,小 A 开车行驶的路程总数与小 B 行驶的路程总数的比值最小(如果小 B 的行驶路程为 0,此时的比值可视为无穷大,且两个无穷大视为相等)。如果从多个城市出发,小 A 开车行驶的路程总数与小 B 行驶的路程总数的比值都最小,则输出海拔最高的那个城市。
  2. 对任意给定的 X=Xi 和出发城市 Si,小 A 开车行驶的路程总数以及小 B 行驶的路程总数。

分析

对于这道题只要处理处理出了第一进和第二进,那么从一个点出发每次先走一个第一进再走一个第二进,很显然这是一条链,那么就可以愉快的玩LCA倍增大法了,
重点就愉快的变成处理第一进和第二进,
既然如此,那么我们就先排个序,每次找到比当前小(大)的 并且在位置我右边的点,再找一个比当前小(大)的 并且在位置我右边的点,就是第一进和第二进的点,
并查集大法,每次找左边和右边的最近,找完后把当前的点染黑,也就是把原本指向自己的左(右)指针向左(右)移一位,
PS:注意开longlong(int64)

AC代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<vector>
#include<algorithm>
#define foi(i,a,b) for(i=a;i<=b;i++)
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fo1(i,a,b) for(i=a;i>=b;i--)
//#define read(a) scanf("%d",&a)
using namespace std;
typedef long long ll;
typedef double db;
const int N=100500,M=18;
const ll FAIL=4340410370284600380;
int read(int &n)
{
    char ch=getchar();
    while((ch!='-')&&((ch<'0')||(ch>'9')))ch=getchar();
    int q=0,w=1;if(ch=='-')w=-1,ch=getchar();
    while(ch>='0' && ch<='9')q=q*10+ch-48,ch=getchar();n=q*w;return n;
}
int m,n,ans;
ll x,y;
int h[N];
int gl[N],gr[N];
int er[N];
struct qw{ll v,n;}G[N];
int a[N][2];//B:0 A:1
ll g[N][M][3];
bool PD(qw a,qw b){return a.v<b.v;}
int gfl(int q){return(q<1?0:(gl[q]!=q?gfl(gl[q]):q));}
int gfr(int q){return(q>n?n+1:(gr[q]!=q?gfr(gr[q]):q));}
void lca(int q,int e)
{
    while(1)
    {
        if(q==n)return;
        int i=0;
        while(x+y+g[q][i+1][0]+g[q][i+1][1]<=e && g[q][i+1][2]!=FAIL)i++;
        if(i==0 && x+y+g[q][i][0]+g[q][i][1]>e)break;
        x+=g[q][i][0],y+=g[q][i][1];
        q=g[q][i][2];
    }
    if(x+y+g[q][0][1]<=e)y+=g[q][0][1];
}
int main()
{
    int q,w;
    read(n);
    fo(i,1,n) read(h[i]),gl[i]=gr[i]=i,G[i].v=h[i],G[i].n=i;
    sort(G+1,G+1+n,PD);G[0].v=-FAIL,G[n+1].v=FAIL;
    fo(i,1,n)er[G[i].n]=i;
    fo(i,1,n)
    {
        int I=er[i];
        int l1=gfl(I-1),r1=gfr(I+1);
        int l2=gfl(l1-1),r2=gfr(r1+1);
        q=l1;w=l2;
        if(abs(h[i]-G[q].v)>G[r1].v-h[i])w=q,q=r1;
            else if(abs(h[i]-G[w].v)>G[r1].v-h[i])w=r1;
        if(abs(h[i]-G[q].v)>G[r2].v-h[i])w=q,q=r2;
            else if(abs(h[i]-G[w].v)>G[r2].v-h[i])w=r2;
        a[i][0]=G[q].n,a[i][1]=G[w].n;
        gl[I]=l1,gr[I]=r1;
    }
    memset(g,60,sizeof(g));
    fo(i,1,n-1)
    {
        if(a[a[i][1]][0])g[i][0][0]=abs(h[a[i][1]]-h[a[a[i][1]][0]]);
        g[i][0][1]=abs(h[i]-h[a[i][1]]),g[i][0][2]=a[a[i][1]][0];
    }
    g[n-1][0][1]=FAIL;
    fo(j,1,M-1)
        fo(i,1,n)
        {
            q=g[i][j-1][2];
            if(q<=n+1)
            {
                g[i][j][2]=g[q][j-1][2];
                if(g[i][j-1][0]!=FAIL && g[q][j-1][0]!=FAIL)g[i][j][0]=g[i][j-1][0]+g[q][j-1][0];
                if(g[i][j-1][1]!=FAIL && g[q][j-1][1]!=FAIL)g[i][j][1]=g[i][j-1][1]+g[q][j-1][1];
            }
        }
    int x0;
    ll ans0=0,ans1=0;
    double aaa=FAIL;
    read(x0);ans=0;
    fo(i,1,n-1)
    {
        x=y=0;
        lca(i,x0);
        if(ans0==0 && x==0)if(h[ans]<h[i])ans=i;
        if(ans0==0 ||(1.0*y/(db)(x))<aaa ||(1.0*y/(db)(x)==aaa && h[ans]<h[i]))ans=i,ans0=x,ans1=y,aaa=1.0*ans1/ans0;
    }
    printf("%d\n",ans);
    int _;
    read(_);
    while(_--)
    {
        read(q),read(w);
        x=y=0;
        lca(q,w);
        printf("%lld %lld\n",y,x);
    }
    return 0;
}

你可能感兴趣的:(倍增,NOIP提高)