CH5701 开车旅行(倍增dp+set)
传送门
解题思路:
一道比较有趣的题,解题工作主要分为两块:
①找出k(k=0表示小A先走,k=1表示小B先走,下面同理)从点i出发下一个到达的点to[k][i];
一开始偷懒用了vector(偷懒一时爽),由于vector的erase操作是o(n)的,这个预处理时间复杂度就彪到o(n2)了。这里改成set就可以将复杂度降到o(nlogn),用链表的话讲道理可以降到o(n)但是排序就要o(nlogn)毫无*用。具体操作就是二分再前后比较(简称瞎搞)。
②两个人从点i出发走2j步走各自开车所走过的路程分别表示为da[i][j][k],db[i][j][k],最终达到点表示为f[i][j][k]。da[i][j][k]表示k从i点出发走了2j步的小A要开车的路程。那么da[i][j][k]+db[i][j][k]就表示了k先手开车从点i出发走了2j步共走过的路程。预处理出第一步和第二步,(转移方程见代码)。
运用二进制拆分的思想,假设出发点为s,p表示当前到达的点,ta、tb分别表示小A和小B从s出发达到p点各自走过的路程,x表示能走得最大路程。从log(最大步数)到0遍历j,如果ta+tb+da[p][j][k]+db[p][j][k]<=x,则令ta和tb分别加上da[p][j][k]和db[p][j][k],p=f[i][j][k]。枚举所有的点进行上述操作就可得到结果。
丑陋的代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+;
typedef long long ll;
ll da[maxn][][],db[maxn][][];map <int,int> m;
int h[maxn],to[][maxn],f[maxn][][];set <int> v;
int main()
{
int n;scanf("%d",&n);
for(int i=;i<=n;i++)
scanf("%d",&h[i]),v.insert(h[i]),m[h[i]]=i;
for(int i=;i<n;i++){
v.erase(h[i]);
auto id=v.lower_bound(h[i]);auto id1=id,id2=id;
if(id==v.begin()){
to[][i]=m[(*id)];
id1++;
if(id1!=v.end())
to[][i]=m[(*id1)];
}
else if(id==v.end()){
id1--;
if(id!=v.begin())
to[][i]=m[(*id1)];
if(id1!=v.begin())
id1--,to[][i]=m[(*id1)];
}
else{
id1--;
int t1=abs(h[i]-(*id)),t2=abs(h[i]-(*id1));
if(t1>t2){
to[][i]=m[(*id1)];id2=id1;id2--;
int t3=(id1!=v.begin())?abs(h[i]-(*id2)):2e9;
if(t1>=t3) to[][i]=m[(*id2)];
else if(t1<t3) to[][i]=m[(*id)];
}
else if(t1<t2){
to[][i]=m[(*id)];id2++;
int t3=(id2!=v.end())?abs(h[i]-(*id2)):2e9;
if(t2>t3) to[][i]=m[(*id2)];
else if(t2<=t3) to[][i]=m[(*id1)];
}
else{
to[][i]=m[(*id1)];
to[][i]=m[(*id)];
}
}
}
for(int i=;i<=n;i++)
f[i][][]=to[][i],f[i][][]=to[][i];
for(int i=;i<=n;i++)
for(int k=;k<=;k++)
f[i][][k]=f[f[i][][k]][][-k];
for(int j=;j<=;j++)
for(int i=;i<=n;i++)
for(int k=;k<=;k++)
f[i][j][k]=f[f[i][j-][k]][j-][k];
for(int i=;i<=n;i++)
da[i][][]=abs(h[(to[][i]==)?i:to[][i]]-h[i]),
db[i][][]=abs(h[(to[][i]==)?i:to[][i]]-h[i]);
for(int i=;i<=n;i++)
for(int k=;k<=;k++)
da[i][][k]=da[i][][k]+da[f[i][][k]][][-k],
db[i][][k]=db[i][][k]+db[f[i][][k]][][-k];
for(int j=;j<=;j++)
for(int i=;i<=n;i++)
for(int k=;k<=;k++)
da[i][j][k]=da[i][j-][k]+da[f[i][j-][k]][j-][k],
db[i][j][k]=db[i][j-][k]+db[f[i][j-][k]][j-][k];
int x;scanf("%d",&x);int ans;double mi=1e18;
for(int s=;s<n;s++){
int p=s;ll ta=,tb=;
for(int j=;j>=;j--)
if(ta+tb+da[p][j][]+db[p][j][]<=x){
ta+=da[p][j][];
tb+=db[p][j][];
p=f[p][j][];
}
double t;if(tb==) t=1e18-;
else t=(double)1.0*ta/tb;
if(t<mi) ans=s,mi=t;
}
printf("%d\n",ans);
int q;scanf("%d",&q);
while(q--){
int s;scanf("%d%d",&s,&x);
int p=s;ll ta=,tb=;
for(int j=;j>=;j--)
if(ta+tb+da[p][j][]+db[p][j][]<=x){
ta+=da[p][j][];
tb+=db[p][j][];
p=f[p][j][];
}
printf("%d %d\n",ta,tb);
}
}
CH5701 开车旅行(倍增dp+set)的更多相关文章
- CH5701 开车旅行
题意 5701 开车旅行 0x50「动态规划」例题 描述 小A和小B决定利用假期外出旅行,他们将想去的城市从1到N编号,且编号较小的城市在编号较大的城市的西边,已知各个城市的海拔高度互不相同,记城市 ...
- P1081 [NOIP2012]开车旅行[倍增]
P1081 开车旅行 题面较为啰嗦.大概概括:一个数列,只能从一个点向后走,两种方案:A.走到和自己差的绝对值次小的点B.走到和自己差的绝对值最小点:花费为此差绝对值:若干询问从规定点向后最多花 ...
- $Noip2012\ Luogu1081$ 开车旅行 倍增优化$ DP$
Luogu Description Sol 1.发现对于每个城市,小A和小B的选择是固定的,可以预处理出来,分别记为ga[],gb[] 2.并且,只要知道了出发城市和出发天数,那么当前城市和小A,小B ...
- 【vijos1780】【NOIP2012】开车旅行 倍增
题目描述 有\(n\)个城市,第\(i\)个城市的海拔为\(h_i\)且这\(n\)个城市的海拔互不相同.编号比较大的城市在东边.两个城市\(i,j\)之间的距离为\(|h_i-h_j|\) 小A和小 ...
- P1081 开车旅行[倍增](毒瘤题)
其实就是个大模拟. 首先,根据题意,小A和小B从任意一个城市开始走,无论\(X\)如何,其路径是一定唯一的. 显然对于两问都可以想出一个\(O(n^2)\)的暴力,即直接一步一步地向右走. 首先,我们 ...
- 洛谷1081 (NOIp2012) 开车旅行——倍增预处理
题目:https://www.luogu.org/problemnew/show/P1081 预处理从每个点开始a能走多少.b能走多少.可以像dp一样从后往前推. 但有X的限制.所以该数组可以变成倍增 ...
- Luogu1081 NOIP2012 开车旅行 倍增
题目传送门 为什么NOIP的题目都这么长qwq 话说2012的D1T3和D2T3都是大火题啊qwq 预处理神题 对于这种跳跳跳的题目考虑使用倍增优化枚举.先预处理某个点之后距离最小和次小的城市,然后倍 ...
- 洛谷P1081 开车旅行(倍增)
题意 题目链接 Sol 咕了一年的题解.. 并不算是很难,只是代码有点毒瘤 \(f[i][j]\)表示从\(i\)号节点出发走了\(2^j\)轮后总的距离 \(da[i][j]\)同理表示\(a\)的 ...
- 洛谷 P1081 开车旅行 —— 倍增
题目:https://www.luogu.org/problemnew/show/P1081 真是倍增好题! 预处理:f[i][j] 表示从 i 点开始走 2^j 次 AB (A,B各走一次)到达的点 ...
随机推荐
- python飞机大战简单实现
小游戏飞机大战的简单代码实现: # 定义敌机类 class Enemy: def restart(self): # 重置敌机的位置和速度 self.x = random.randint(50, 400 ...
- 记一次:Windows的Socket编程学习和分析过程
Socket编程依赖于:WS2_32.dll --- 服务端 --- .导入我们需要的函数 #incldue <windows.h> //#include<WinSock2.h> ...
- Android项目目录结构模板以及简单说明
1) src 文件 编写java代码的文件目录,遵循java的命名规范.分包 2) gen 文件 包含了android的资源文件的标识符,是不需要程序员维护,是自动添加的 3) asse ...
- Python-函数练习题1
# coding=utf-8 '''定义一个方法get_num(num),num参数是列表类型,判断列表里面的元素为数字类型.其他类型则报错, 并且返回一个偶数列表:(注:列表里面的元素为偶数).'' ...
- 99%的程序员都在用Lombok,原理竟然这么简单?我也手撸了一个!|建议收藏!!!
罗曼罗兰说过:世界上只有一种英雄主义,就是看清生活的真相之后依然热爱生活. 对于 Lombok 我相信大部分人都不陌生,但对于它的实现原理以及缺点却鲜为人知,而本文将会从 Lombok 的原理出发,手 ...
- 快速理解编码,unicode与utf-8
1.为什么编码,因为cpu只认识数字2.ASCII 一个字符共占7位,用一个字节表示,共128个字符3.那么ASCII浪费了最高位多可惜,出现了ISO-8859-1,一个字节,256个字符,很多协议的 ...
- SpannableStringBuilder实现TextView华丽变身
前言 想要在TextView 的文本添加一些突出文字,然后点击可以进行跳转,首先想到的就是TextView拼接,但是考虑到换行后的显示又不是很合适,一番查询后发现了SpannableStringBui ...
- Flume数据采集结合etcd作为配置中心在爬虫数据采集处理中的架构实践。
Apache Flume是一个分布式的.可靠的.可用的系统,用于有效地收集. 聚合和将大量日志数据从许多不同的源移动到一个集中的数据存储,但是其本身是以本地properties作为配置的,配置无法做到 ...
- 2020年最新版Web前端学习路线图-前端小白入门必读-pink老师推荐
Hello,大家好,相信很多学习前端的小伙伴,会有很多的疑惑: 我要学习那些技术? 我要到哪里去学习这些技术呢? 学习这些技术的目的对就业有什么帮助呢? 我学到什么程度可以胜任工作? ... 这里,p ...
- 一位萌新Google冲浪的开始
这一切的开始可能都来源于对 百度 各方面的不满吧(确实不咋滴) 于是开始对Google感冒,上必应https://cn.bing.com/去搜了下“国内如何上Google”,上面也是众说纷纭,莫衷一是 ...