来源:NOIP2012提高组Day1

题意:

有n个城市,第i个城市可以通向所有j城市(j>i),每个城市有一个海拔h[i],两个城市之间的距离定义为d(x,y)=abs(h[x]-h[y])。
有两个人,小A和小B轮流开车,小A第一个开。
小A的开车习惯是开到与当前城市距离次小的城市。
小B的开车习惯是开到与当前城市距离最小的城市。
如果轮到某个人的时候他们开不下去了,或者开去的代价会使得总代价大于x,那么就停止驾驶。
首先给出一个x[0],求出从哪个城市出发使得cost[a]/cost[b]最小。注意如若cost[b]=0,那么这个值视为无穷大,且两个无穷大是一样大的。如果存在比值相等的情况,那么输出海拔最高的那个城市编号。
然后给出m个询问。
求对于s[i],x[i],从s[i]出发,总代价不超过x[i],a,b各走了多少距离。
注意:
1.如果对于某个城市有两个城市与它的距离一样大,那么认为海拔较低的离它近。
2.城市的海拔各不相同。

n<=100000,m<=10000,x[i],h[i]在int范围内,1<=s[i]<=n

分析

题面好长
首先显而易见的可以发现对于同一个点同一个人,走到的结果肯定是一样的。x对走向的下一个点是没有影响的。也就是说从某个点开始走的路径是唯一的,只是x决定了这条路能走多长。

第一问我没有想到什么特别好的做法,只能选择暴力枚举每一个点来找最优的。

对于70%的数据的做法。
n<=1000,m<=10000
可以用O( n2 n^2)的方法求出对于每个点向后走向的最小点和次小点是哪个,然后对于第一问直接枚举每个点,然后对于每个(s,x)都用O(n)的跑一次。事实上时间会比n要小一些不过还是肯定会T的根本不用想…第二问是同理的。

#include<cstdio>
#include<algorithm>
#include<cstring>
#define M 100005
#define ll long long
#define pll pair<long long,long long>
#define A first
#define B second
#define mp make_pair
using namespace std;void read(int &x){x=0; char c=getchar(); int f=1;for (; c<'0'; c=getchar())if (c=='-')f=-1;for (; c>='0'; c=getchar())x=(x<<3)+(x<<1)+(c^'0');  x*=f;
}
int h[M],n,m,X[M],S[M];
struct AAA{ll mx[M],mx1[M];pll get(ll s,ll x){
//      printf("---%d %d---\n",s,x);
//      printf("%d",s);
//      printf("%lld %lld\n",s,x);ll coa=0,cob=0,nex,co;bool f=0;for (; x&&s;){if (f){nex=mx[s];  co=abs(h[nex]-h[s]);if (!nex){x=0; continue;}if (nex&&co<=x)x-=co,cob+=co;else x=0;s=nex;}else{nex=mx1[s]; co=abs(h[nex]-h[s]);if (!nex){x=0; continue;}if (nex&&co<=x)x-=co,coa+=co;else x=0;s=nex;}f=!f;}return mp(coa,cob); }void solve(){int i,j;for (i=1; i<=n; i++){mx[i]=mx1[i]=0;for (j=i+1; j<=n; j++){int d=abs(h[i]-h[j]);if (mx[i]==0||d<abs(h[i]-h[mx[i]])||(d==abs(h[i]-h[mx[i]])&&h[j]<h[mx[i]])){mx1[i]=mx[i]; mx[i]=j;}else if(mx1[i]==0||d<abs(h[i]-h[mx1[i]])||(d==abs(h[i]-h[mx1[i]])&&h[j]<h[mx1[i]]))mx1[i]=j;}
//          printf("%lld %lld\n",mx[i],mx1[i]);}   ll res=-1;ll ca=0,cb=0;for (i=1; i<=n; i++){pll now=get(i,X[0]);if (now.B){if (res==-1||now.A*cb<now.B*ca||(now.A*cb==now.B*ca&&h[i]>h[res])){res=i;ca=now.A;cb=now.B;}}else {if (res==-1||(h[i]>h[res]&&cb==0))res=i;}}printf("%lld\n",res);for (i=1; i<=m; i++){pll now=get(S[i],X[i]);printf("%lld %lld\n",now.A,now.B);}}
}p70;int main(){int i;
//  freopen("drive.in","r",stdin);
//  freopen("drive.out","w",stdout);read(n);for (i=1; i<=n; i++)read(h[i]);read(X[0]);read(m);for (i=1; i<=m; i++)read(S[i]),read(X[i]);p70.solve();return 0;
}

对于100%的数据的做法
首先来看一下上面的做法,复杂度主要是在两个方面,一个是对于每一个点求之后的最小值和次小值,一个是对于每个(s,x)都要用O(n)的复杂度来模拟,太费时间了。

对于第一个求最小值和次小值的优化。似乎有两种方法。
1.用数据结构维护,快速的找比它小的最大的点和比它大的最小的点,还有次大和次小。不过我并没有写这个。
2.链表,排序依次然后将城市依次按海拔顺序连起来,每次求完一个点就把它给删了。且每个点的答案只会在它的左节点,左节点的左节点,右节点和右节点的右节点中出现,判断一下即可。

对于第二个的优化。可以采用倍增的方法。
设to[x][k]表示从x出发,走了 2k 2^k轮后到达的位置。
co[x][k].A表示从x出发,走了 2k 2^k轮后小A的路程。
co[x][k].B表示从x出发,走了 2k 2^k轮后小B的路程。
然后模拟的复杂度就降为O(log n)了。

#include<cstdio>
#include<algorithm>
#include<cstring>
#define M 100005
#define ll long long
#define pll pair<long long,long long>
#define A first
#define B second
#define inf (1e18)
#define mp make_pair
using namespace std;void read(ll &x){x=0; char c=getchar(); int f=1;for (; c<'0'; c=getchar())if (c=='-')f=-1;for (; c>='0'; c=getchar())x=(x<<3)+(x<<1)+(c^'0'); x*=f;
}
ll h[M],to[M][18],n,m,X[M],S[M],Mp[M];
pll co[M][18];
struct AC{ll x,ne,be;}c[M];
pll b[M],a[M];void Max(ll &x,ll y){if (x<y)x=y;}
void Min(ll &x,ll y){if (x>y)x=y;}int ch(int x){//二分查找 用于离散(似乎手写比lb要快...) int l=1,r=n,mid;for (; ;){mid=(l+r)>>1;if (b[mid].A==x)return mid;if (b[mid].A<x)l=mid+1;else r=mid-1;       }
}void del(int x){//删除链表中的节点 c[c[x].be].ne=c[x].ne;c[c[x].ne].be=c[x].be;c[x].ne=c[x].be=0;c[0].be=c[0].ne=c[n+1].be=c[n+1].ne=0;
}bool ch(int y,int z,int x){if (z==0)return 0;if (y==0||y==n+1)return 1;x=h[x]; y=h[y]; z=h[z];int l1=abs(b[y].A-b[x].A),l2=abs(b[z].A-b[x].A);if (l1<l2)return 0;if (l1>l2)return 1;//对于距离相等的情况,海拔低的比较近 return y>z;
}pll get(ll s,ll x){ll ca=0,cb=0,k;//倍增模拟 for (k=16; k>=0&&x&&s; k--)if (to[s][k]&&co[s][k].A+co[s][k].B<=x){x-=co[s][k].A+co[s][k].B;ca+=co[s][k].A;cb+=co[s][k].B;s=to[s][k];}//小A可能可以再走一次 if (s&&co[s][0].A<=x){x-=co[s][0].A; ca+=co[s][0].A;}return mp(ca,cb);
}int main(){int i;
//  freopen("drive.in","r",stdin);
//  freopen("drive.out","w",stdout);read(n);for (i=1; i<=n; i++)read(h[i]),b[i].A=h[i],b[i].B=i;sort(b+1,b+n+1);for (i=1; i<=n; i++)h[i]=ch(h[i]),Mp[h[i]]=i;c[0].x=inf; c[n+1].x=inf;for (i=1; i<=n; i++){c[b[i].B].x=i;c[b[i].B].be=b[i-1].B;c[b[i].B].ne=b[i+1].B;}int be1,be2,ne1,ne2;for (i=1; i<=n; i++){be1=c[i].be;be2=c[c[i].be].be;ne1=c[i].ne;ne2=c[c[i].ne].ne;int res1=0,res2=0;if (ch(res1,be1,i)){res2=res1; res1=be1;}else if (ch(res2,be1,i))res2=be1;if (ch(res1,be2,i)){res2=res1; res1=be2;}else if (ch(res2,be2,i))res2=be2;if (ch(res1,ne1,i)){res2=res1; res1=ne1;}else if (ch(res2,ne1,i))res2=ne1;if (ch(res1,ne2,i)){res2=res1; res1=ne2;}else if (ch(res2,ne2,i))res2=ne2;if (res1>n)res1=0; if (res2>n)res2=0;a[i].A=res1; a[i].B=res2;del(i);}b[0].A=inf; b[n+1].A=inf;for (i=1; i<=n;i++){to[i][0]=a[a[i].B].A;co[i][0].A=abs(b[h[i]].A-b[h[a[i].B]].A);co[i][0].B=abs(b[h[a[i].B]].A-b[h[a[a[i].B].A]].A);}int k;for (k=1; k<17; k++){for (i=1; i<=n; i++){to[i][k]=to[to[i][k-1]][k-1];co[i][k].A=co[i][k-1].A+co[to[i][k-1]][k-1].A;co[i][k].B=co[i][k-1].B+co[to[i][k-1]][k-1].B;  }}ll x,s;read(x);int res=-1;ll ca=0,cb=0;for (i=1; i<=n; i++){pll now=get(i,x);if (now.B){if (res==-1||now.A*cb<now.B*ca||(now.A*cb==now.B*ca&&h[i]>h[res])){res=i;ca=now.A;cb=now.B;}}else {if (res==-1||(h[i]>h[res]&&cb==0))res=i;}}printf("%d\n",res);read(m);for (i=1; i<=m; i++){read(s); read(x);pll now=get(s,x);printf("%lld %lld\n",now.A,now.B);}return 0;
}

吐槽

码量好像有点大啊…而且再加上前一题有个高精度除法…我估计比赛就算知道正解也未必敲的出来
而且因为嫌麻烦好多不需要long long的也用long long了…不过实在是懒得修改了。

开车旅行(drive)相关推荐

  1. NOIP2012 开车旅行

    开车旅行 (drive.cpp/c/pas) [问题描述] 小 A 和小 B 决定利用假期外出旅行,他们将想去的城市从 1 到 N 编号,且编号较小的城市在编号较大的城市的西边,已知各个城市的海拔高度 ...

  2. NOIP2012提高组 开车旅行 解题报告

    开车旅行 题目描述 样例输入 样例输出 70分算法 暴力预处理出对于每一个点他右边最近.次近的点的编号,对于每一个询问,暴力模拟开车过程即可. 100算法 和上面一样我们得预处理出每一个点最近.次近的 ...

  3. 刷题记录(NC16562 开车旅行)(树上倍增)

    NC16562 开车旅行 题目链接 关键点: 1.预处理:将所有点到达另一个点的最短和次短距离先求出 方法:利用set,从最后一个城市开始插入,每次插入到set中,查看左右是否存在有城市(即set是按 ...

  4. NOIP2012 开车旅行 (倍增)

    题意:一行N个城市,有各自不同的海拔,定义两个城市之间的距离为海拔之差的绝对值,小a和小b轮流开车,开车方向从左往右,小a总是开到第二近的城市,小b开到最近的城市(如有两个城市和当前城市海拔之差相等, ...

  5. NOIP2012开车旅行 【倍增】

    题目 小 A 和小 B 决定利用假期外出旅行,他们将想去的城市从 1 到 N 编号,且编号较小的城市在编号较大的城市的西边,已知各个城市的海拔高度互不相同,记城市 i 的海拔高度为Hi,城市 i 和城 ...

  6. 【NOIP2012提高组】开车旅行

    Description 现在有n个城市,每个城市有它的高度 Hi H_i,保证每个 Hi H_i互不相同.我们定义两个城市之间的距离 disi,j=|Hi−Hj| dis_{i,j}=|H_i-H_j ...

  7. NOIP2012DAY1T3【开车旅行】

    Description 小A和小B决定利用假期外出旅行,他们将想去的城市从1到N编号,且编号较小的城市在编号较大的城市的西边,已知各个城市的海拔高度互不相同,记城市i 的海拔高度为Hi,城市i 和城市 ...

  8. 题解 P1081 【开车旅行】

    小 A \text{A} A 和小 B \text{B} B 决定利用假期外出旅行,他们将想去的城市从 $1 $ 到 n n n 编号,且编号较小的城市在编号较大的城市的西边,已知各个城市的海拔高度互 ...

  9. NOIP 2012 开车旅行

    题目描述 小 A 和小 B 决定利用假期外出旅行,他们将想去的城市从 1 到 N 编号,且编号较小的 城市在编号较大的城市的西边,已知各个城市的海拔高度互不相同,记城市 i 的海拔高度为 Hi,城市 ...

最新文章

  1. 前端页面速度统计方法
  2. twsited快速基础
  3. php fastcgi,配置apache以fastcgi运行php
  4. 【Vegas原创】ASP 0131 不允许父路径的解决
  5. VVDocumenter 使用
  6. 【好文链接】环形队列、串口数据处理
  7. 将Ajax请求返回的json数据,转换成json对象
  8. 开启OpenStack Api跨域请求(CORS)功能
  9. Ocelot中文文档-Raft(实验功能不能用于生产环境)
  10. 设计python游戏贪吃蛇_Python 贪吃蛇游戏
  11. 几款流行的电路仿真软件简介与学习资源分享
  12. 【Arduino创意】基于蜂鸣器制作摩尔斯电码生成器
  13. Compiere 开源CRM的比较选择
  14. CVPR 2021 目标检测论文大盘点(65篇论文)
  15. Git 修改commit提交信息
  16. 更改RStudio快捷键
  17. srsLTE系统安装教程
  18. 薇电子学与计算机好中吗,微电子学与计算机是什么级别的期刊
  19. 网易2018校园招聘:魔法币 [python]
  20. 1KB (Kilobyte 千字节)=1024B=8192b【大B代表Byte字节,小b代表bit位】

热门文章

  1. 关于服务器重启之后无法启动perforce服务的问题
  2. 抖音计算机表白文档,抖音上的表白代码是什么 抖音表白代码自动生成方法
  3. C1见习工程师认证C1-01任务记录教学
  4. 在线部分:werobot服务、主要逻辑服务、句子相关模型服务、BERT中文预训练模型+微调模型(目的:比较两句话text1和text2之间是否有关联)、模型在Flask部署
  5. nacos1.1.3登录显示用户名密码错误
  6. dnf没有机器人引爆_DNF:看完这4点,我终于明白了其他职业为何与机械师差距这么大...
  7. 高级 IO(非阻塞、多路复用、异步、存储映射、文件锁)
  8. 枚举属性和不可枚举属性
  9. 微信小程序华容道最优解
  10. HTML-summary