vijos P1780 【NOIP2012】 开车旅行

描述

小\(A\)和小\(B\)决定利用假期外出旅行,他们将想去的城市从\(1\)到\(N\)编号,且编号较小的城市在编号较大的城市的西边,已知各个城市的海拔高度互不相同,记城市\(i\)的海拔高度为\(H_i\),城市\(i\)和城市\(j\)之间的距离\(d(i,j)\)恰好是这两个城市海拔高度之差的绝对值,即\(d(i,j) = |H_i - H_j|\)。

旅行过程中,小$A$和小$B$轮流开车,第一天小$A$开车,之后每天轮换一次。他们计划选择一个城市$S$作为起点,一直向东行驶,并且最多行驶$X$公里就结束旅行。小$A$和小$B$的驾驶风格不同,小$B$总是沿着前进方向选择一个最近的城市作为目的地,而小$A$总是沿着前进方向选择第二近的城市作为目的地(注意:本题中如果当前城市到两个城市的距离相同,则认为离海拔低的那个城市更近)。如果其中任何一人无法按照自己的原则选择目的城市,或者到达目的地会使行驶的总距离超出$X$公里,他们就会结束旅行。 
在启程之前,小$A$想知道两个问题: 
1.对于一个给定的$X=X_0$,从哪一个城市出发,小$A$开车行驶的路程总数与小$B$行驶的路程总数的比值最小(如果小$B$的行驶路程为$0$,此时的比值可视为无穷大,且两个无穷大视为相等)。如果从多个城市出发,小$A$开车行驶的路程总数与小$B$行驶的路程总数的比值都最小,则输出海拔最高的那个城市。 
2. 对任意给定的$X=X_i$和出发城市$S_i$,小$A$开车行驶的路程总数以及小$B$行驶的路程总数。

输入格式

第一行包含一个整数$N$,表示城市的数目。 
第二行有$N$个整数,每两个整数之间用一个空格隔开,依次表示城市$1$到城市$N$的海拔高度,即$H_1,H_2,……,H_n,$且每个$H_i$都是不同的。 
第三行包含一个整数$X_0$。 
第四行为一个整数$M$,表示给定$M$组$S_i$和$X_i$。 
接下来的$M$行,每行包含$2$个整数$S_i$和$X_i$,表示从城市$S_i$出发,最多行驶$X_i$公里。

输出格式

输出共$M+1$行。 
第一行包含一个整数$S_0$,表示对于给定的$X_0$,从编号为$S_0$的城市出发,小$A$开车行驶的路程总数与小$B$行驶的路程总数的比值最小。 
接下来的$M$行,每行包含$2$个整数,之间用一个空格隔开,依次表示在给定的$S_i$和$X_i$下小$A$行驶的里程总数和小$B$行驶的里程总数。

提示

对于$30\%$的数据,有$1\leq N\leq 20,1\leq M\leq 20$; 
对于$40\%$的数据,有$1\leq N\leq 100,1\leq M\leq 100$; 
对于$50\%$的数据,有$1\leq N\leq 100,1\leq M\leq 1000$;
对于$70\%$的数据,有$1\leq N\leq 1000,1\leq M\leq 10000$;
对于$100\%$的数据,有$1\leq N\leq 10^5,1\leq M\leq 10^4,-10^9\leq H\leq 10^9,0\leq X_0\leq 10^9,1\leq S_i\leq N,0\leq Xi\leq 10^9$,数据保证$H_i$互不相同。

  这道题是很久以前刷$noip$题的时候留下来未解决的,现在来填个坑……

  一开始我写的是平衡树,现在看了$NOI$年鉴之后才意识到可以使用排序加链表来解决……

  我们先考虑如果我们知道了每个位置往后最近的城市和次近的城市,那么我们可以使用倍增来解决这道题。我们处理出每个位置往后跳$2^i$所在的位置以及此时小$A$走过的距离和小$B$走过的距离,第二问的每个询问就可以在$O(\log n)$的复杂度内得到解决。而第一位又可以枚举起点算一算,所以,我们主要是要找出每个点往后会走到哪儿。

  当然,我们可以考虑从后往前扫一遍,那么我们需要支持的操作有两个:加入一个值,以及查询比某个值大的最小、次小值,比这个值小的最大、次大值。这样的话我们就需要一颗平衡树。虽然直接调用$set$即可解决,但是常数非常之大,我之前就在$vijos$上被卡$TLE$了一个点,并且死活过不去。

  我们现在来考虑一种新的方法。其实这种方法在今年$noip$的初赛中就已经出现了。我们可以把所有的高度排好序,并且记录下原位置。然后,我们按照排序之后的顺序构建一个双向链表,按原顺序扫一遍。在这个双向链表中我们可以非常轻易地找出我们所需要的信息,并且每次用完这个点之后我们需要将它删除,在这个双向链表中也可以在常数复杂度内做到。所以,我们就没有必要使用平衡树了。

  下面贴代码(代码有点丑):

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
#define maxn 100010
#define INF (1LL<<60) using namespace std;
typedef long long llg; struct data{
llg x;int y;
data(llg a=0,int b=0):x(a),y(b){}
}a[5];
int wh[maxn],n,m,pr[maxn],ne[maxn],ns;
int X,to[maxn][17],na[maxn],nb[maxn],fa[maxn],fb[maxn];
llg ca,cb,va[maxn][17],vb[maxn][17],h[maxn];
double now; int getint(){
int w=0;bool q=0;
char c=getchar();
while((c>'9'||c<'0')&&c!='-') c=getchar();
if(c=='-') c=getchar(),q=1;
while(c>='0'&&c<='9') w=w*10+c-'0',c=getchar();
return q?-w:w;
} bool operator < (data x,data y){
if(x.x!=y.x) return x.x<y.x;
return h[x.y]<h[y.y];
} void qsort(int l,int r){
int i=l,j=r,mid=h[wh[(l+r)>>1]];
while(i<=j){
while(h[wh[i]]<mid) i++;
while(h[wh[j]]>mid) j--;
if(i<=j) swap(wh[i++],wh[j--]);
}
if(i<r) qsort(i,r);
if(j>l) qsort(l,j);
} void pre(){
wh[n+1]=ne[n+1]=n+1; h[0]=-INF; h[n+1]=INF;
for(int i=1;i<=n;i++) ne[wh[i]]=wh[i+1],pr[wh[i]]=wh[i-1];
for(int i=1;i<=n;i++){
a[1]=data(h[ne[i]]-h[i],ne[i]); a[2]=data(h[ne[ne[i]]]-h[i],ne[ne[i]]);
a[3]=data(h[i]-h[pr[i]],pr[i]); a[4]=data(h[i]-h[pr[pr[i]]],pr[pr[i]]);
if(pr[i]>=1) ne[pr[i]]=ne[i]; if(ne[i]<=n) pr[ne[i]]=pr[i];
sort(a+1,a+5); nb[i]=a[1].y%(n+1); na[i]=a[2].y%(n+1);
fa[i]=a[2].x; fb[i]=a[1].x;
}
for(int i=1;i<=n;i++){
if(na[i]) va[i][0]=fa[i],to[i][0]=na[i];
if(nb[na[i]]) va[i][1]=va[i][0],vb[i][1]=fb[na[i]],to[i][1]=nb[na[i]];
}
for(int j=2;j<=16;j++){
for(int i=1;i<=n;i++){
if(to[to[i][j-1]][j-1]){
va[i][j]=va[i][j-1]+va[to[i][j-1]][j-1];
vb[i][j]=vb[i][j-1]+vb[to[i][j-1]][j-1];
to[i][j]=to[to[i][j-1]][j-1];
}
}
}
} double cal(){
if(!cb) return (1LL<<60);
return (double)ca/(double)cb;
} void solve(int S,int X){
ca=cb=0;
for(int i=16;i>=0;i--)
if(to[S][i] && ca+cb+va[S][i]+vb[S][i]<=X)
ca+=va[S][i],cb+=vb[S][i],S=to[S][i];
} int main(){
File("a");
n=getint();
for(int i=1;i<=n;i++) h[i]=getint(),wh[i]=i;
qsort(1,n); pre(); now=1e100; X=getint();
for(int i=1;i<=n;i++){
solve(wh[i],X); double x=cal();
if(x<=now) now=x,ns=wh[i];
}
printf("%d\n",ns);
m=getint();
while(m--){
int s=getint(),x=getint(); solve(s,x);
printf("%lld %lld\n",ca,cb);
}
return 0;
}

  几个提交的地方:vijos P1780codevs 1199

上一篇:POJ 2186 Popular Cows tarjan缩点算法


下一篇:codevs 1115 开心的金明--01背包