【NOIP2012】Day1T3 开车旅行
2016-10-30 15:07
330 查看
开车旅行
Description小A和小B决定利用假期外出旅行,他们将想去的城市从1到N编号,且编号较小的城市在编号较大的城市的西边,已知各个城市的海拔高度互不相同,记城市i的海拔高度为Hi,城市i 和城市j 之间的距离d[i,j]恰好是这两个城市海拔高度之差的绝对值,即d[i,j]=∣∣Hi−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行驶的路程总数。
Input Format
输入文件为drive.in。
第一行包含一个整数N,表示城市的数目。
第二行有N个整数,每两个整数之间用一个空格隔开,依次表示城市1到城市N的海拔高度,即H1,H2,……Hn,且每个Hi 都是不同的。
第三行包含一个整数X0。
第四行为一个整数M,表示给定M组Si和Xi。
接下来的M行,每行包含2个整数Si 和Xi,表示从城市Si 出发,最多行驶Xi 公里。
Output Format
输出文件为drive.out。
输出共M+1行。
第一行包含一个整数S0,表示对于给定的X0,从编号为S0的城市出发,小A开车行驶的路程总数与小B行驶的路程总数的比值最小。
接下来的M行,每行包含2个整数,之间用一个空格隔开,依次表示在给定的Si 和Xi 下小A行驶的里程总数和小B行驶的里程总数。
Sample Input
样例1
4
2 3 1 4
3
4
1 3
2 3
3 3
4 3
样例2
10
4 5 6 1 2 3 7 8 9 10
7
10
1 7
2 7
3 7
4 7
5 7
6 7
7 7
8 7
9 7
10 7
Sample Output
样例1
1
1 1
2 0
0 0
0 0
样例2
2
3 2
2 4
2 1
2 4
5 1
5 1
2 1
2 0
0 0
0 0
Hint
【输入输出样例1说明】
各个城市的海拔高度以及两个城市间的距离如上图所示。
如果从城市1出发,可以到达的城市为2,3,4,这几个城市与城市1的距离分别为1,1,2,但是由于城市3的海拔高度低于城市2,所以我们认为城市3离城市1最近,城市2离城市1第二近,所以小A会走到城市2。到达城市2后,前面可以到达的城市为3,4,这两个城市与城市2的距离分别为2,1,所以城市4离城市2最近,因此小B会走到城市4。到达城市4后,前面已没有可到达的城市,所以旅行结束。
如果从城市2出发,可以到达的城市为3,4,这两个城市与城市2的距离分别为2,1,由于城市3离城市2第二近,所以小A会走到城市3。到达城市3后,前面尚未旅行的城市为4,所以城市4离城市3最近,但是如果要到达城市4,则总路程为2+3=5>3,所以小B会直接在城市3结束旅行。
如果从城市3出发,可以到达的城市为4,由于没有离城市3第二近的城市,因此旅行还未开始就结束了。
如果从城市4出发,没有可以到达的城市,因此旅行还未开始就结束了。
【输入输出样例2说明】
当X=7时,
如果从城市1出发,则路线为1 -> 2 -> 3 -> 8 -> 9,小A走的距离为1+2=3,小B走的距离为1+1=2。(在城市1时,距离小A最近的城市是2和6,但是城市2的海拔更高,视为与城市1第二近的城市,所以小A最终选择城市2;走到9后,小A只有城市10可以走,没有第2选择可以选,所以没法做出选择,结束旅行)
如果从城市2出发,则路线为2 -> 6 -> 7,小A和小B走的距离分别为2,4。
如果从城市3出发,则路线为3 -> 8 -> 9,小A和小B走的距离分别为2,1。
如果从城市5出发,则路线为5 -> 7 -> 8,小A和小B走的距离分别为5,1。
如果从城市6出发,则路线为6 -> 8 -> 9,小A和小B走的距离分别为5,1。
如果从城市7出发,则路线为7 -> 9 -> 10,小A和小B走的距离分别为2,1。
如果从城市8出发,则路线为8 -> 10,小A和小B走的距离分别为2,0。
如果从城市9出发,则路线为9,小A和小B走的距离分别为0,0(旅行一开始就结束了)。
如果从城市10出发,则路线为10,小A和小B走的距离分别为0,0。
从城市2或者城市4出发小A行驶的路程总数与小B行驶的路程总数的比值都最小,但是城市2的海拔更高,所以输出第一行为2。
【数据范围】
对于30%的数据,有1≤N≤20,1≤M≤20;
对于40%的数据,有1≤N≤100,1≤M≤100;
对于50%的数据,有1≤N≤100,1≤M≤1,000;
对于70%的数据,有1≤N≤1,000,1≤M≤10,000;
对于100%的数据,有1≤N≤100,000,1≤M≤10,000,−1,000,000,000≤Hi≤1,000,000,000,0≤X0≤1,000,000,000,1≤Si≤N,0≤Xi≤1,000,000,000,数据保证Hi 互不相同。
分析
题意明了,思路清晰,就是维护一下每个城市的最短和次短城市,然后直接做就可以了。在维护最短和次短时,可以用双向链表(我使用线段树)。前进的时候需要用到倍增的思想,跳跃式前进。
#include<cstdio> #include<cstring> #include<algorithm> #include<map> #define N 100005 #define LL long long using namespace std; struct Data{int d;LL v;}p ; struct note{int max,min;}t[N*5]; inline bool cmp(const Data x,const Data y){return x.v<y.v||x.v==y.v&&x.d<y.d;} double sum,ans; int n,m,tot,x,y,k; int h ,a ,b ,w ,g [18]; LL f [18][2],ansa,ansb,v ; void change(int v,int l,int r,int x){ if (l==r){t[v].max=t[v].min=l;return;} int m=(l+r)/2; if (x<=m) change(v*2,l,m,x); else change(v*2+1,m+1,r,x); t[v].max=max(t[v*2].max,t[v*2+1].max); t[v].min=min(t[v*2].min,t[v*2+1].min); } int Getmax(int v,int l,int r,int x,int y){ if (x>y) return 0; if (l==x && r==y) return t[v].max; int mid=(l+r)/2; if (y<=mid) return Getmax(v*2,l,mid,x,y); if (x>=mid+1) return Getmax(v*2+1,mid+1,r,x,y); return max(Getmax(v*2,l,mid,x,mid),Getmax(v*2+1,mid+1,r,mid+1,y)); } int Getmin(int v,int l,int r,int x,int y){ if (x>y) return n+1; if (l==x && r==y) return t[v].min; int mid=(l+r)/2; if (y<=mid) return Getmin(v*2,l,mid,x,y); if (x>=mid+1) return Getmin(v*2+1,mid+1,r,x,y); return min(Getmin(v*2,l,mid,x,mid),Getmin(v*2+1,mid+1,r,mid+1,y)); } void solve(int x,int y){ ansa=ansb=0; for (int i=17;i>=0;i--) if (f[x][i][0]+f[x][i][1]<=y){ y-=f[x][i][0]+f[x][i][1]; ansa+=f[x][i][0];ansb+=f[x][i][1]; x=g[x][i]; } if (f[x][0][0]<=y) ansa+=f[x][0][0]; } int main(){ freopen("in.txt","r",stdin); freopen("out.txt","w",stdout); scanf("%d",&n); for (int i=1;i<=n;i++) scanf("%lld",&v[i]),p[i].v=v[i],p[i].d=i; sort(p+1,p+n+1,cmp); v[0]=1<<31; for (int i=1;i<=n;i++) h[p[i].d]=++tot,w[tot]=p[i].d; for (int i=1;i<=5*n;i++) t[i].min=n+1; for (int i=n;i;i--){ p[1].d=Getmin(1,1,n,h[i]+1,n); p[2].d=Getmax(1,1,n,1,h[i]-1); p[3].d=Getmin(1,1,n,p[1].d+1,n); p[4].d=Getmax(1,1,n,1,p[2].d-1); for(int j=1;j<=4;j++) p[j].v=abs(v[i]-v[w[p[j].d]]); sort(p+1,p+5,cmp); if (p[1].d!=0&&p[1].d!=n+1) b[i]=w[p[1].d]; if (p[2].d!=0&&p[2].d!=n+1) a[i]=w[p[2].d]; change(1,1,n,h[i]); } for (int i=1;i<=n;i++){ g[i][0]=b[a[i]]; f[i][0][0]=abs(v[i]-v[a[i]]); f[i][0][1]=abs(v[a[i]]-v[b[a[i]]]); } for (int j=1;j<=17;j++) for (int i=1;i<=n;i++){ g[i][j]=g[g[i][j-1]][j-1]; f[i][j][0]=f[i][j-1][0]+f[g[i][j-1]][j-1][0]; f[i][j][1]=f[i][j-1][1]+f[g[i][j-1]][j-1][1]; } scanf("%d",&x); ans=1<<30; for (int i=1;i<=n;i++){ solve(i,x); if (!ansb) sum=1<<30; else sum=ansa*1.0/ansb; if (sum<ans || (sum==ans && v[i]>v[k])) ans=sum,k=i; } printf("%d\n",k); for(scanf("%d",&m);m;m--) { scanf("%d%d",&x,&y); solve(x,y); printf("%lld %lld\n",ansa,ansb); } fclose(stdin); fclose(stdout); }
相关文章推荐
- 【noip 2012】提高组Day1T3.开车旅行
- NOIP2012 开车旅行 (倍增)
- 2012Noip提高组Day1 T3 开车旅行
- NOIP2012 开车旅行
- [NOIP2012]开车旅行
- Luogu 1081 【NOIP2012】开车旅行 (链表,倍增)
- NOIP2012开车旅行
- NOIP2012开车旅行
- NOIP 2012 开车旅行
- 洛谷 P1081 开车旅行 [noip2012] (倍增lca+链表优化)
- noip2012 开车旅行
- noip2012 P1081 开车旅行
- [NOIP2012] 提高组 洛谷P1081 开车旅行
- vijos P1780 【NOIP2012】 开车旅行
- Cogs 1264. [NOIP2012] 开车旅行(70分 暴力)
- [Codevs 1199][NOIP 2012提高组]开车旅行
- 【NOIP2012提高组】开车旅行
- NOIP2012提高组 开车旅行 题解+代码
- Noip 2012 开车旅行 - 倍增DP
- NOIP2012 T3开车旅行 set+倍增