【NOIP2012提高组】开车旅行

时间:2022-12-20 21:34:43

Description

现在有n个城市,每个城市有它的高度Hi,保证每个Hi互不相同。我们定义两个城市之间的距离 dis(i,j)=|HiHj| ,并且只能从编号小的城市去到编号大的城市。现在有两个人,小A和小B要开车(雾)去旅行。小A先开一天,小B再开一天。每一天都可以从一个开到另一个城市。小A会选择去离当前城市第二近的城市,小B会选择去离当前城市最近的那个城市。如果他们行驶的总路程将会超过给定的X就会不继续开车(∩_∩),结束旅行。求:
1:给定一个X,求从哪一个城市出发,小A行驶的路程/小B行驶的路程最小。(认为一个数/0=∞)。若有多个城市相等,选择高度最高的那个。
2:给出m个询问,每次询问从S出发,限制为X,小A走的路程和小B走的路程

Solution

注意小A和小B开同一辆车,感觉超级坑,TAT

用什么

题目很显然,就是用一个数据结构维护最小值和次小值,可以维护的数据结构有很多啦,这么经典的要求。可以用什么双向链表啊,权值线段树啊(因为下标线段树不容易转移值域,而权值线段树很容易),平衡树啊等等。

怎么做

打个权值线段树练练手,维护这段值域区间的坐标最大值和最小值(权值线段树的经典维护方法)。
为什么两个都要维护呢?
因为,我要在左边找相近的,可能比a[i]小,可能比a[i]大,所以要找a[i]+1到max的坐标小的,和1到a[i]-1找一个大的。一共有四个值,所以排一个序之后,在取前两个,打c++就是好。
然后,因为走得路程在给出的范围下是固定的,所以,可以打经典的倍增算法,像个lca一样。

离散化!

不离散化,要爆啊!!!

开long long啊!

这个吃分啊,吃了70多分。

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fod(i,a,b) for(i=a;i>=b;i--)
#define ll long long
using namespace std;
const int maxn=100007;
ll i,j,k,l,n,m,x0;
double ans,ans1;
ll a[maxn];
struct node{
    int l,r;
}t[maxn*10];
struct noo{
    int o,p,q;
}op[maxn*10];
struct nod{
    ll a,b;
}pai[maxn*10];
int f[maxn][18];
ll g[maxn][18][2],ans2,xx0,ber,ber1;
ll b[maxn],c[maxn],d[maxn],e[maxn];
void insert(int x,int l,int r,int y){
    if(l==r){
        t[x].l=t[x].r=l;
    }
    else{
        int mid=(l+r)/2;
        if(y<=mid)insert(x*2,l,mid,y);
        else insert(x*2+1,mid+1,r,y);
        t[x].l=min(t[x*2].l,t[x*2+1].l);
        t[x].r=max(t[x*2].r,t[x*2+1].r);
    }
}
int dexiao(int x,int l,int r,int y,int z){
    if(y>z)return n+1;
    if(l==y&&r==z){
        return t[x].l; 
    }
    else{
        int mid=(l+r)/2;
        if(z<=mid)return dexiao(x*2,l,mid,y,z);
        else if(y>mid)return dexiao(x*2+1,mid+1,r,y,z);
        else{
            return min(dexiao(x*2,l,mid,y,mid),dexiao(x*2+1,mid+1,r,mid+1,z));    
        }
    }
}
int deda(int x,int l,int r,int y,int z){
    if(y>z)return 0;
    if(l==y&&r==z){
        return t[x].r; 
    }
    else{
        int mid=(l+r)/2;
        if(z<=mid)return deda(x*2,l,mid,y,z);
        else if(y>mid)return deda(x*2+1,mid+1,r,y,z);
        else{
            return max(deda(x*2,l,mid,y,mid),deda(x*2+1,mid+1,r,mid+1,z));    
        }
    }
}
void doing(int x,int y){
    int i;
    ber1=ber=0;
    fod(i,17,0){
        if(g[x][i][0]+g[x][i][1]<=y){
            y-=(g[x][i][0]+g[x][i][1]);
            ber=ber+g[x][i][0];ber1=ber1+g[x][i][1];
            x=f[x][i];
        }
    }
    if(g[x][0][0]<=y)ber+=g[x][0][0];
}
bool cmp(nod x,nod y){return x.b<y.b||x.b==y.b&&x.a<y.a;}
bool cmp1(noo x,noo y){return x.o<y.o;}
int main(){
    scanf("%d",&n);
    a[0]=0x7fffffff;
    fo(i,1,n){
        scanf("%lld",&a[i]);
        op[i].o=a[i];op[i].p=i;
    }
    sort(op+1,op+n+1,cmp1);
    fo(i,1,n)d[op[i].p]=i,e[i]=op[i].p;
    fo(i,1,n*10)t[i].l=n+1,t[i].r=0; 
    fod(i,n,1){
        pai[1].a=dexiao(1,1,n,d[i]+1,n);
        pai[2].a=deda(1,1,n,1,d[i]-1);
        pai[3].a=dexiao(1,1,n,pai[1].a+1,n);
        pai[4].a=deda(1,1,n,1,pai[2].a-1);
        fo(j,1,4)pai[j].b=abs(a[i]-a[e[pai[j].a]]);
        sort(pai+1,pai+5,cmp);
        if(pai[2].a!=0&&pai[2].a!=1+n)b[i]=e[pai[2].a];
        if(pai[1].a!=0&&pai[1].a!=1+n)c[i]=e[pai[1].a];
        insert(1,1,n,d[i]);
    }
    fo(i,1,n){
        f[i][0]=c[b[i]];
        g[i][0][0]=abs(a[i]-a[b[i]]);
        g[i][0][1]=abs(a[b[i]]-a[c[b[i]]]);
    }
    fo(i,1,17){
        fo(j,1,n){
            f[j][i]=f[f[j][i-1]][i-1];
            g[j][i][0]=g[f[j][i-1]][i-1][0]+g[j][i-1][0];
            g[j][i][1]=g[f[j][i-1]][i-1][1]+g[j][i-1][1];

        }
    }
    ans=0x7fffffff;
    scanf("%d",&xx0);
    fo(i,1,n){
        doing(i,xx0);
        if(ber1!=0)ans1=ber*1.0/ber1;else ans1=0x7fffffff;
        if(ans1<ans||ans1==ans&&a[ans2]<a[i]) ans=ans1,ans2=i;
    }
    printf("%d\n",ans2);
    scanf("%d",&m);
    fo(i,1,m){
        scanf("%d%d",&k,&l);
        ber=ber1=0;
        doing(k,l);
        printf("%lld %lld\n",ber,ber1);
    }
}