• 【NOIP2012】开车旅行(倍增)


    题面

    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

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

    Output

    输出共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

    题解

    NOIP2012真的都是好题。。。
    因为无论从那个点开始
    接下来的选择都是固定的
    因此,可以首先预处理出每一次的选择
    继续观察,若干次的移动之后的地点也是不会变的
    照样可以预处理出来
    因此,很容易想到倍增
    在跳倍增的时候,一边跳地点,一边跳时间,即可算出最终答案。
    第一问就依次查询每一个位置
    第二问,每次查询即可

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<set>
    #include<map>
    #include<vector>
    using namespace std;
    #define MAX 101000
    #define INF 50000000000
    #define ll long long
    inline ll read()
    {
    	ll x=0,t=1;char ch=getchar();
    	while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    	if(ch=='-')t=-1,ch=getchar();
    	while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    	return x*t;
    }
    //p[i][j]表示从i出发经过2^j轮后的地点
    //a[i][j]表示从i出发经过2^j轮后的路程
    //b[i][j]表示从i出发经过2^j轮后的B的路程
    struct Sort
    {
    	int v,id;
    }dd[MAX];
    inline bool operator <(Sort a,Sort b)
    {
    	return a.v<b.v;
    }
    int N,M,id[MAX];
    int lst[MAX],nst[MAX];
    long long p[MAX][20],b[MAX][20],a[MAX][20],d[MAX];
    ll dis[MAX][2],pos[MAX][2];
    inline void kill(int x)
    {
    	lst[nst[x]]=lst[x];
    	nst[lst[x]]=nst[x];
    }
    inline void check(int i,int l1)
    {
    	ll Dis=abs(d[i]-dd[l1].v);
    	if(dis[i][0]>Dis||(dis[i][0]==Dis&&dd[l1].v<d[pos[i][0]]))
    	{
    		swap(dis[i][0],dis[i][1]);swap(pos[i][0],pos[i][1]);
    		dis[i][0]=Dis;pos[i][0]=dd[l1].id;
    	}
    	else
    	{
    		if(dis[i][1]>Dis||(dis[i][1]==Dis&&dd[l1].v<d[pos[i][1]]))
    		{
    			dis[i][1]=Dis;pos[i][1]=dd[l1].id;
    		}
    	}
    }
    inline void Pre()//预处理
    {
    	for(int i=1;i<=N;++i)//处理某个点的最近和次近
    	{
    		int node=id[i];
    		pos[i][0]=pos[i][1]=0;dis[i][0]=dis[i][1]=INF;
    		int l1=lst[node];int l2=lst[l1];
    		int r1=nst[node];int r2=nst[r1];
    		if(l1!=0&&l1!=N+1)check(i,l1);
    		if(l2!=0&&l2!=N+1)check(i,l2);
    		if(r1!=0&&r1!=N+1)check(i,r1);
    		if(r2!=0&&r2!=N+1)check(i,r2);
    		kill(node);
    	}
    	for(int i=1;i<=N;++i)
    	{
    		p[i][0]=pos[pos[i][1]][0];
    		a[i][0]=dis[i][1];
    		b[i][0]=dis[pos[i][1]][0];
    	}
    	for(int j=1;j<=17;++j)
    	{
    		for(int i=1;i<=N;++i)
    		{
    			p[i][j]=p[p[i][j-1]][j-1];
    			a[i][j]=a[i][j-1]+a[p[i][j-1]][j-1];
    			b[i][j]=b[i][j-1]+b[p[i][j-1]][j-1];
    		}
    	}
    }
    inline void Query(int s,ll x,long long &ans1,long long &ans2)
    {
        ans1=0,ans2=0;
    	for(int i=17;i>=0;--i)
    	{
    		if(p[s][i]&&a[s][i]+b[s][i]<=x)
    		{
    			x-=a[s][i]+b[s][i];
    			ans1+=a[s][i];
    			ans2+=b[s][i];
    			s=p[s][i];
    		}
    	}
    	//检查A还能不能单独跳一步
    	if(pos[s][1]&&dis[s][1]<=x)ans1+=dis[s][1];
    }
    int main()
    {
    	N=read();
    	for(int i=1;i<=N;++i)dd[i].v=d[i]=read(),dd[i].id=i;
    	sort(&dd[1],&dd[N+1]);
    	for(int i=1;i<=N;++i)id[dd[i].id]=i;
    	for(int i=1;i<=N;++i)nst[i]=i+1;
    	for(int i=1;i<=N;++i)lst[i]=i-1;
    	lst[0]=nst[N+1]=0;
    	nst[0]=1;lst[N+1]=N;
    	Pre();
    	int X0=read();
    	double nn=2147483647.0;
    	int A=0;
    	for(int i=1;i<=N;++i)
    	{
    		long long ans1,ans2;
    		double tt;
    		Query(i,X0,ans1,ans2);
    		if(ans2==0)continue;
    		if(ans2!=0)tt=(ans1*1.0)/(ans2*1.0);
    		else tt=1.0;
    		if(nn>tt){nn=tt;A=i;}
    		else if(nn==tt)if(d[i]>d[A])A=i;
    	}
    	printf("%d
    ",A);
    	M=read();
    	int cnt=0;
    	while(M--)
    	{
    		cnt++;
    		ll s=read(),x=read();
    		long long ans1=0,ans2=0;
    		Query(s,x,ans1,ans2);
    		printf("%lld %lld
    ",ans1,ans2);
    	}
    	return 0;
    }
    
    
  • 相关阅读:
    2017年前端开发者应该重拾基本技能学习
    手机号码月消费档次API
    实用且免费API接口2
    在线文档转换API word,excel,ppt等在线文件转pdf、png
    火车票抢票API 根据乘客的车次与座席要求快速订票出票
    利用问答机器人API开发制作聊天类App
    用聚合数据API(苏州实时公交API)快速写出小程序
    OllyDbg使用笔记
    解决git commit 大文件推送失败
    每日一语
  • 原文地址:https://www.cnblogs.com/cjyyb/p/7531809.html
Copyright © 2020-2023  润新知