NOIP 2012 洛谷P1081 开车旅行
Description:
就是两个人开车,只能向东开。向东有n个城市,城市之间的距离为他们的高度差。A,B轮流开车,A喜欢到次近的城市,B喜欢到最近的城市。如果车子开到底了或者车子开的路程已经超过了限制X就停。
问你从一个点出发,最后A行驶的里程数和B行驶的里程数。
倍增的妙用,这道题改变了我对NOIP的看法。让我对着书看了好久才看懂
不过70分还是好拿的,就是预处理然后对每个询问$O(n)$ 模拟一遍。复杂度$O(nlog_{2}n+nm)$
怎么预处理?就是找到一个城市$i$后离他最近的城市和次近的城市,分别为$gb[i]$,$ga[i]$,用平衡树(set)或者链表或者权值线段树实现
满分在70分的基础上,倍增预处理,然后$O(log_{2}n)$回答每个询问
对于第一个询问,枚举起点即可。
接下来, $dp[i][j][k]$表示$k$在$j$点出发,共开$2^i$天的车到达的城市,1表示A,2表示B。
$sta[i][j][k]$表示$k$在$j$点出发,共开$2^i$天的车A所行驶的路程,$stb[i][j][k]$表示$k$在$j$点出发,共开$2^i$天的车B所行驶的路程。
边界和初值:$dp[0][j][0]=ga[j]$,$dp[0][j][1]=gb[j]$,$sta[0][j][0]=dist(j,ga[j])$,$stb[0][j][1]=dist(j,gb[j])$ 其他都为0
转移看代码,特别注意的是因为$i=1$时,两人是换着开的,所以转移的时候k要去个反。而$i>1$的话,$2^i$天和$2^{i-1}$时开车的人是相同的故不用取反
其他对着状态就能理解了把,就不写注释了。
/*代码修改自李煜东霸霸,变量名是按照书上说法开的*/
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + ;
#define ll long long
ll sta[][N][], stb[][N][], ansA, ansB, la, lb;
int dp[][N][], n, m, ga[N], gb[N], h[N], i, t, ans;
struct node{ int x, y;} ;
set<node> s;
set<node>::iterator it,lt,rt;
int dist(int x, int y){ return abs(h[x] - h[y]); }
bool cmp(int x, int y){ return dist(x, i) == dist(y, i) ? h[x] < h[y] : dist(x, i) < dist(y, i); }
bool operator < (node a, node b){ return a.y < b.y; }
void solve(int s, int X){
la = lb = ; int k = ;
for(int j = t; j >= ; j--)
if(dp[j][s][k] && sta[j][s][k] + stb[j][s][k] <= X){
X -= (sta[j][s][k] + stb[j][s][k]);
la += sta[j][s][k], lb += stb[j][s][k];
if(j == ) k ^= ;
s = dp[j][s][k];
}
}
int main(){
scanf("%d", &n);
for(int i = ; i <= n; i++) scanf("%d", &h[i]);
for(i = n; i >= ; i--){
node tmp; tmp.x = i, tmp.y = h[i];
int temp[];
s.insert(tmp); it = s.find(tmp);
lt = it, rt = it, m = ;
if(lt != s.begin()) lt--, temp[++m] = lt -> x;
if(lt != s.begin()) lt--, temp[++m] = lt -> x;
if(rt++, rt != s.end()){
temp[++m] = rt -> x;
if(rt++, rt != s.end()) temp[++m] = rt -> x;
}
sort(temp + , temp + m + , cmp);
if(m) gb[i] = temp[];
if(m > ) ga[i] = temp[];
}
t = log(n * 1.0) / log(2.0);
for(i = ; i <= n; i++){
if(ga[i]) dp[][i][] = ga[i], sta[][i][] = dist(ga[i], i), stb[][i][] = ;
if(gb[i]) dp[][i][] = gb[i], stb[][i][] = dist(gb[i], i), sta[][i][] = ;
}
for(i = ; i <= t; i++)
for(int j = ; j <= n; j++)
for(int k = ; k < ; k++){
int l;
if(i == ) l = k ^ ; else l = k;
if(dp[i - ][j][k]) dp[i][j][k] = dp[i - ][dp[i - ][j][k]][l];
if(dp[i][j][k]){
sta[i][j][k] = sta[i - ][j][k] + sta[i - ][dp[i - ][j][k]][l];
stb[i][j][k] = stb[i - ][j][k] + stb[i - ][dp[i - ][j][k]][l];
}
}
int X0;
scanf("%d", &X0); ansA = , ansB = ;
for(i = ; i <= n; i++){
solve(i, X0);
if(!lb) la = ;
if(la * ansB < lb * ansA || (la * ansB == lb * ansA && h[i] > h[ans]))
ansA = la, ansB = lb, ans = i;
}
printf("%d\n", ans);
scanf("%d", &m);
int x, y;
while(m--){
scanf("%d%d", &x, &y);
solve(x, y);
printf("%lld %lld\n", la, lb);
}
return ;
}
倍增优化DP就是一个划分状态+状态拼凑的过程
NOIP 2012 洛谷P1081 开车旅行的更多相关文章
- 洛谷 P1081 开车旅行(70)
P1081 开车旅行 题目描述 小AA 和小BB 决定利用假期外出旅行,他们将想去的城市从 11到 NN 编号,且编号较小的城市在编号较大的城市的西边,已知各个城市的海拔高度互不相同,记城市 ii的海 ...
- [NOIP2012] 提高组 洛谷P1081 开车旅行
题目描述 小 A 和小 B 决定利用假期外出旅行,他们将想去的城市从 1 到 N 编号,且编号较小的 城市在编号较大的城市的西边,已知各个城市的海拔高度互不相同,记城市 i 的海拔高度为 Hi,城市 ...
- 洛谷 P1081 开车旅行 —— 倍增
题目:https://www.luogu.org/problemnew/show/P1081 真是倍增好题! 预处理:f[i][j] 表示从 i 点开始走 2^j 次 AB (A,B各走一次)到达的点 ...
- 2018.11.04 洛谷P1081 开车旅行(倍增)
传送门 思路简单码量超凡? 感觉看完题大家应该都知道是倍增sbsbsb题了吧. 首先预处理出从每个点出发如果是AAA走到哪个点,如果是BBB走到哪个点. 然后利用刚刚预处理出的信息再预处理从每个点出发 ...
- 洛谷P1081 开车旅行(倍增)
题意 题目链接 Sol 咕了一年的题解.. 并不算是很难,只是代码有点毒瘤 \(f[i][j]\)表示从\(i\)号节点出发走了\(2^j\)轮后总的距离 \(da[i][j]\)同理表示\(a\)的 ...
- 洛谷P1081 开车旅行70分
https://www.luogu.org/problem/show?pid=1081 太遗憾了明明写出来了,却把最小值初始值弄小了,从第二个点开始就不可能对了.70分! #include<io ...
- 洛谷 P1081 开车旅行【双向链表+倍增】
倍增数组的20和N写反了反复WAWAWA-- 注意到a和b在每个点上出发都会到一个指定的点,所以这样构成了两棵以n点为根的树 假设我们建出了这两棵树,对于第一问就可以枚举起点然后倍增的找出ab路径长度 ...
- 洛谷P1081 开车旅行
题目 双向链表+倍增+模拟. \(70pts\): 说白了此题的暴力就是细节较多的模拟题. 我们设离\(i\)城市最近的点的位置为\(B[i]\),第二近的位置为\(A[i]\).设\(A\)或\(B ...
- 洛谷P1081——开车旅行
传送门:QAQQAQ 题意注意点: 1.是从前往后走,不能回头 2.小A小B轮流开,先小A开,而小A是到第二近的点(这点调试的时候查了好久) 3.若绝对值差相同海拔低的更近,而第一个询问若比值相同是海 ...
随机推荐
- 跟浩哥学自动化测试Selenium -- Selenium简介 (1)
Selenium 简介 Selenium 是一款开源的web自动化测试工具,用来模拟对浏览器的操作(主要是对页面元素的操作),简单来讲,其实就是一个jar包.Selenium早期的版本比如1.0市场占 ...
- 使用Mininet创建网络拓扑
使用Mininet创建Topo Python脚本实现创建拓扑 #coding:utf-8 from mininet.net import Mininet from mininet.topo impor ...
- 基于zookeeper+mesos+marathon的docker集群管理平台
参考文档: mesos:http://mesos.apache.org/ mesosphere社区版:https://github.com/mesosphere/open-docs mesospher ...
- PowerShell自定义修改远程桌面RDP端口
应朋友的要求写了一个通过PowerShell修改远程桌面(Remote Desktop)端口的脚本,不复杂,启动脚本后有两个选项:1.自定义远程桌面:2.回复远程桌面的默认端口3389 发出来给有用的 ...
- three的初步探索之表象篇
首先three.js是啥?用来干啥的?首先在谈这个之前,先说下canvas,canvas是h5新生的一个功能,可以用来画图,表达许多更绚丽的特效,然后canvas目前有个软当,就是只能2d,不支持三维 ...
- php命名空间学习笔记。
为什么要用命名空间? 在PHP中,命名空间用来解决在编写类库或应用程序时创建可重用的代码如类或函数时碰到的两类问题: 用户编写的代码 与 PHP内部的类/函数/常量或第三方类/函数/常量之间的名字冲 ...
- Polycarp and Letters(set首战!)
Description Polycarp loves lowercase letters and dislikes uppercase ones. Once he got a string s con ...
- 2017软工第十周个人PSP
11.17--11.23本周例行报告 1.PSP(personal software process )个人软件过程. C(类别) C(内容) ST(开始时间) ET(结束时间) INT(间隔时间) ...
- SDN练习一
SDN练习第一题 题目描述 实现网络拓扑: 具体要求: 南向接口采用OpenFlow 协议. 可查看网络的拓扑信息视图. H1.H2.H3.H4 任意两两可互通. 实现思路 利用mininet可视化图 ...
- DB2定位锁等待
在应用中,我们经常会碰到sql执行很慢,但是数据库cpu和内存使用率又不高的情况,类似的问题基本上由于锁,排序等原因造成,本文主要描述如何去定位锁等待问题,谁在锁等待?等待谁持有的锁?锁在那个表? 一 ...