题面

​ 这个题目还是值得思考的.

​ 看到这个题目, 大家应该都想到了这样一个思路, 就是把每个点能够达到的最近的和次近的点都预处理出来, 然后跑就可以了, 现在问题就是难在这个预处理上面, 我们应该如何做呢? 观察到, 近的概念是两点之间的海拔距离最小, 所以我们可以将海拔距离从后往前(毕竟你只能往你后面的地方走嘛...), 扔进一个可以维护大小关系的数据结构中, 不妨设当前点海拔进入这个数据结构后位置为\(k\), 那么我们只需要比较位置为\(k - 1\), \(k - 2\), \(k + 1\), \(k + 2\)的四个数就可以了, 大家可以自己在纸上分类讨论一下, 这里就不做过多的阐述了. 现在我们只需要知道这个数据结构就可以了, 大家想一想, 有什么数据结构可以快速的找到某个数排序后的位置呢? 平衡树, 查找和插入的复杂度都为\(\log_ {2}{n}\)了, 这样我们就可以快速寻找了, 当然, 因为有重复结构, 所以用STL中的\(multiset\)是比较好的一种办法(其实是我不会双向链表), 这样我们就将比较难想的预处理部分弄出来了.

​ 当然, 想完了预处理后我们就可以想接下来怎么走了. 比较快速的方法是倍增, \(f[i][j][opt]\)表示当前位置为\(i\), 已经走了\(2 ^j\)天, \(opt\)为1时代表从\(i\)位置开始走, 第一个走的是B所走到的位置, 那么\(opt\)是\(0\)时就是A了. \(disa[i][j][opt]\)代表在第\(i\)位出发, 走了\(2 ^ j\)天, \(opt\)为1代表从\(i\)开始走, B先出发, 在这段时间中A走的距离, opt为\(0\)自己推一下吧, 实在是不想打了, \(disb[i][j][opt]\)也自己照着看一下吧. 我们在预处理出每个点的最近点和次近点后, 就可以处理出来了.

​ 至此, 预处理完毕, 剩下的代码中会说到的.

具体代码

#include <iostream>
#include <cstring>
#include <cstdio>
#include <set>
#define N 100005
using namespace std; int n, x0, m, f[N][22][2], disa[N][22][2], disb[N][22][2], bin;
struct node
{
int id, h;
bool operator < (const node &p) const { return h < p.h; }
} ht[N]; multiset<node> s;
multiset<node> :: iterator it; inline int read()
{
int x = 0, w = 1;
char c = getchar();
while(c < '0' || c > '9') { if (c == '-') w = -1; c = getchar(); }
while(c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
return x * w;
} inline void get_ans(int S, int &dist_a, int &dist_b, int limit)
{
int now = S;
for(int i = 20; i >= 0; i--)
if(f[now][i][0] && disa[now][i][0] + disb[now][i][0] + dist_a + dist_b <= limit)//倍增跑, 注意当没有能够跑的地方或者超过了给定的值的话就需要continue
{
dist_a += disa[now][i][0];
dist_b += disb[now][i][0];
now = f[now][i][0];
}
} inline long long abs(long long x) { return x < 0 ? -x : x; } inline void init()
{
for(int i = n; i >= 1; i--)
{
int Ato, Bto; node nxt, pre;
s.insert(ht[i]);//先插入当前海拔, 方便找
it = s.lower_bound(ht[i]);//找到当前海拔在序列中的位置
it++; nxt = (*it);
it--; it--; pre = (*it);
it++;
if(abs(nxt.h - ht[i].h) < abs(pre.h - ht[i].h))
{
it++, it++;
Bto = nxt.id;
if(abs(pre.h - ht[i].h) > abs((*it).h - ht[i].h)) Ato = (*it).id;
else Ato = pre.id;
}
else
{//同上
it--, it--;
Bto = pre.id;
if(abs(nxt.h - ht[i].h) >= abs((*it).h - ht[i].h)) Ato = (*it).id;
else Ato = nxt.id;
}
//注意, 这里由于前后海拔差相同的话取海拔较低的, 所以有的地方有等于号, 有的地方没有, 希望大家能够自己好好去想一下为什么, 有问题可以私信我(反正我不会回答的(手动删除)).
f[i][0][0] = Ato; f[i][0][1] = Bto;
disa[i][0][0] = abs(ht[Ato].h - ht[i].h); disb[i][0][0] = 0;
disb[i][0][1] = abs(ht[Bto].h - ht[i].h); disa[i][0][1] = 0;
}
for(int i = 1; i <= n; i++)//由于上面并没有讨论到A, B都走过了的情况, 只讨论了A和B单独走的情况, 故在这里要处理一下.
{
f[i][1][0] = f[f[i][0][0]][0][1];
f[i][1][1] = f[f[i][0][1]][0][0];
disa[i][1][1] = abs(ht[f[i][1][1]].h - ht[f[i][0][1]].h); disb[i][1][0] = abs(ht[f[i][1][0]].h - ht[f[i][0][0]].h);
disa[i][1][0] = disa[i][0][0]; disb[i][1][1] = disb[i][0][1]; //类似于倍增LCA中的预处理
}
for(int j = 2; j <= 20; j++)
for(int i = 1; i <= n; i++)
{
f[i][j][0] = f[f[i][j - 1][0]][j - 1][0];
f[i][j][1] = f[f[i][j - 1][1]][j - 1][1];
disa[i][j][0] = disa[i][j - 1][0] + disa[f[i][j - 1][0]][j - 1][0];
disb[i][j][0] = disb[i][j - 1][0] + disb[f[i][j - 1][0]][j - 1][0];
disa[i][j][1] = disa[i][j - 1][1] + disa[f[i][j - 1][1]][j - 1][0];
disb[i][j][1] = disb[i][j - 1][1] + disb[f[i][j - 1][1]][j - 1][0];
//仔细思考一下, 应该不难
}
} int main()
{
n = read();
for(int i = 1; i <= n; i++) { ht[i].h = read(); ht[i].id = i; }
ht[0].h = 2e9 + 7; ht[n + 1].h = -(2e9 + 7); ht[0].id = 0; ht[n + 1].id = n + 1;
//我取2147483647爆掉了, 调了整整一上午, 所以取极值的时候需要注意一下
s.insert(ht[0]); s.insert(ht[0]); s.insert(ht[n + 1]); s.insert(ht[n + 1]);
//插入两个极小值和极大值代表我们在找到位置k的时候, 他前后总会至少有两个点, 免去了分类讨论的冗杂
init(); //预处理
x0 = read(); m = read();
int id = 0;
double ans = 1e16;
for(int i = 1; i <= n; i++)//枚举每一个点为起点时的比例
{
int dist_a = 0, dist_b = 0;
get_ans(i, dist_a, dist_b, x0);
if(dist_b == 0)
{
if(ans > 1e14) { ans = 1e14; id = i; }
else if(ans == 1e14 && ht[i].h > ht[id].h) id = i;
}
else
{
double res = (double) dist_a / dist_b;
if(res < ans) { ans = res; id = i; }
else if(res == ans && ht[i].h > ht[id].h) id = i;
}
}
printf("%d\n", id);
for(int i = 1; i <= m; i++)
{
bin = read(); x0 = read();
int dist_a = 0, dist_b = 0;
get_ans(bin, dist_a, dist_b, x0);
printf("%d %d\n", dist_a, dist_b);
}
return 0;
}

[luogu1081] 开车旅行的更多相关文章

  1. luogu1081 开车旅行 树上倍增

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

  2. luogu1081 开车旅行2012 D1T3 (倍增,set,O2)

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

  3. $Noip2012\ Luogu1081$ 开车旅行 倍增优化$ DP$

    Luogu Description Sol 1.发现对于每个城市,小A和小B的选择是固定的,可以预处理出来,分别记为ga[],gb[] 2.并且,只要知道了出发城市和出发天数,那么当前城市和小A,小B ...

  4. Luogu 1081 【NOIP2012】开车旅行 (链表,倍增)

    Luogu 1081 [NOIP2012]开车旅行 (链表,倍增) Description 小A 和小B决定利用假期外出旅行,他们将想去的城市从1到N 编号,且编号较小的城市在编号较大的城市的西边,已 ...

  5. CH5701 开车旅行

    题意 5701 开车旅行 0x50「动态规划」例题 描述 小A和小B决定利用假期外出旅行,他们将想去的城市从1到N编号,且编号较小的城市在编号较大的城市的西边,已知各个城市的海拔高度互不相同,记城市 ...

  6. 2012Noip提高组Day1 T3 开车旅行

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

  7. Cogs 1264. [NOIP2012] 开车旅行(70分 暴力)

    1264. [NOIP2012] 开车旅行 ★★☆   输入文件:drive.in   输出文件:drive.out   简单对比时间限制:2 s   内存限制:128 MB [题目描述] 小A 和小 ...

  8. 开车旅行 【NOIP2012 D1T3】

    开车旅行 [NOIP2012 D1T3] 倍增 首先令\(a[i]\)表示从i出发最近的城市下标,\(b[i]\)表示从i出发第二近的城市下标 可以维护一个\(\text{set<pair< ...

  9. 洛谷 P1081 开车旅行(70)

    P1081 开车旅行 题目描述 小AA 和小BB 决定利用假期外出旅行,他们将想去的城市从 11到 NN 编号,且编号较小的城市在编号较大的城市的西边,已知各个城市的海拔高度互不相同,记城市 ii的海 ...

随机推荐

  1. 高并发第八弹:J.U.C起航(java.util.concurrent)

    java.util.concurrent是JDK自带的一个并发的包主要分为以下5部分: 并发工具类(tools) 显示锁(locks) 原子变量类(aotmic) 并发集合(collections) ...

  2. wcf和webapi(转)

    WCF 1.这个也是基于SOAP的,数据格式是XML 2.这个是Web Service(ASMX)的进化版,可以支持各种各样的协议,像TCP,HTTP,HTTPS,Named Pipes, MSMQ. ...

  3. javascript学习笔记(二)

    二.DOM DOM是"Document Object Model"(文档对象模型)的首字母缩写,当创建了一个网页并把它加载到WEB浏览器 中时,DOM就在后台生成,它讲根据你编写的 ...

  4. vue父与子通信

    个人理解并整理如下 一.父传子 prop传参 父组件传递参数<x-scoll :class="red"></x-scoll> 子组件props:[" ...

  5. ArcGis实现画矩形(RectangleFeedBack)

    private void axMapControl1_OnMouseDown(object sender, IMapControlEvents2_OnMouseDownEvent e)         ...

  6. 解决移动端touch事件(touchstart/touchend) 的穿透问题

    情景: 我们在移动端点击事件click对比touchend会有很明显的300ms的延迟,为啥? 浏览器在 click 后会等待约300ms去判断用户是否有双击行为(手机需要知道你是不是想双击放大网页内 ...

  7. Vue 框架-11-介绍src文件流程及根组件app+HBuilder 配置

    Vue 框架-11-介绍src文件流程及根组件app+HBuilder 配置 这是上一篇对目录简单介绍: 关于编辑器,可以使用轻量级的 Sublime Text 3,我使用的是 HBuilder, 但 ...

  8. doPost方法不支持 a 标签和地址栏直接输入地址访问

    demo <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF- ...

  9. windows 命令行报错:file(s) not in client view

    今天在执行p4 sync命令时报错:File(s) not in client view,查找后发现其实是未连接上p4服务器.需要重新设置P4PORT=服务器地址  即可解决(参考链接:https:/ ...

  10. 优化tableView加载cell与model的过程

    优化tableView加载cell与model的过程 效果图 说明 1. 用多态的特性来优化tableView加载cell与model的过程 2. swift写起来果然要比Objective-C简洁了 ...