【vijos1780】【NOIP2012】开车旅行 倍增
题目描述
有\(n\)个城市,第\(i\)个城市的海拔为\(h_i\)且这\(n\)个城市的海拔互不相同。编号比较大的城市在东边。两个城市\(i,j\)之间的距离为\(|h_i-h_j|\)
小A和小B要开车去旅行。小A先开,他们会轮流开车。小A会把车开到第二近的城市,小B会把车开到最近的城市。如果当前城市到两个城市的距离相同,则认为海拔低的城市比较近。他们只会把车往东边开(即编号大的那边)。
小A会先问你对于一个给定的\(x=x_0\),从哪一个城市出发,小A开车行驶的路程总数与小B行驶的路程总数的比值最小(如果小B的行驶路程为\(0\),此时的比值可视为无穷大,且两个无穷大视为相等)。如果从多个城市出发,小A开车行驶的路程总数与小B行驶的路程总数的比值都最小,则输出海拔最高的那个城市。
他还会问你\(m\)次。每次要你求出对给定的\(x=x_i\) 和出发城市\(s_i\),小A开车行驶的路程总数以及小B行驶的路程总数。
\(n\leq 100000,m\leq 10000\)
题解
先用set求出当前在第\(i\)个城市时小A会把车开到哪个城市和小B会把车开到那个城市。
然后倍增。\(fa_{i,j}\)表示当前在第\(i\)个城市时开\(2^j\)轮(小A和小B各开一次算一轮)后小A开车的距离,\(f_b{i,j}\)表示当前在第\(i\)个城市时开\(2^j\)轮后小B开车的距离。\(g_{i,j}\)表示当前在第\(i\)个城市时开\(2^j\)轮后会到达哪里。
现在我们要求当前在第\(s\)个城市,开不超过\(x\)公里,小A开车的路程和小B开车的路程。显然直接倍增就可以了,还要加上不满一轮的情况,即开若干轮后小A再开一次。
第一问直接枚举起点,计算答案。
第二问直接计算答案。
时间复杂度:\(O((n+m)\log n)\)
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<ctime>
#include<utility>
#include<set>
#include<cmath>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
typedef pair<ll,int> pli;
ll h[100010];
set<pli> ht;
int na[100010];
int nb[100010];
ll fa[100010][20];
ll fb[100010][20];
int g[100010][20];
int getmin(ll v)
{
if(ht.size()==0)
return -1;
set<pli>::iterator p=ht.lower_bound(pli(v,0));
if(p==ht.begin())
return p->second;
if(p==ht.end())
{
p--;
return p->second;
}
pii s1=*p;
s1.first-=v;
p--;
pii s2=*p;
s2.first=v-s2.first;
if(s1.first<s2.first)
return s1.second;
return s2.second;
}
struct p
{
int x;
double s;
};
double inf=1e100;
int main()
{
freopen("vijos1780.in","r",stdin);
freopen("vijos1780.out","w",stdout);
int n;
scanf("%d",&n);
int i,j;
for(i=1;i<=n;i++)
scanf("%lld",&h[i]);
for(i=n;i>=1;i--)
{
nb[i]=getmin(h[i]);
if(nb[i]==-1)
na[i]=-1;
else
{
ht.erase(ht.find(pii(h[nb[i]],nb[i])));
na[i]=getmin(h[i]);
ht.insert(pii(h[nb[i]],nb[i]));
}
ht.insert(pii(h[i],i));
}
for(i=1;i<=n;i++)
if(na[i]==-1)
{
g[i][0]=i;
fa[i][0]=fb[i][0]=0;
}
else if(nb[na[i]]==-1)
{
g[i][0]=na[i];
fa[i][0]=abs(h[i]-h[na[i]]);
fb[i][0]=0;
}
else
{
g[i][0]=nb[na[i]];
fa[i][0]=abs(h[i]-h[na[i]]);
fb[i][0]=abs(h[na[i]]-h[nb[na[i]]]);
}
for(j=1;j<=19;j++)
for(i=1;i<=n;i++)
{
g[i][j]=g[g[i][j-1]][j-1];
fa[i][j]=fa[i][j-1]+fa[g[i][j-1]][j-1];
fb[i][j]=fb[i][j-1]+fb[g[i][j-1]][j-1];
}
int k;
ll x0;
scanf("%lld",&x0);
p ans;
ans.x=-1;
for(i=1;i<=n;i++)
{
ll sa=0,sb=0;
j=i;
ll s=0;
for(k=19;k>=0;k--)
if(fa[j][k]+fb[j][k]+s<=x0)
{
s+=fa[j][k]+fb[j][k];
sa+=fa[j][k];
sb+=fb[j][k];
j=g[j][k];
}
if(na[j]!=-1&&s+abs(h[j]-h[na[j]])<=x0)
sa+=abs(h[j]-h[na[j]]);
double si;
if(!sb)
si=inf;
else
si=double(sa)/sb;
if(ans.x==-1||si<ans.s||(si==ans.s&&h[i]>h[ans.x]))
{
ans.x=i;
ans.s=si;
}
}
printf("%d\n",ans.x);
int m;
scanf("%d",&m);
for(i=1;i<=m;i++)
{
int x;
ll x0;
scanf("%d%lld",&x,&x0);
j=x;
ll s=0;
ll sa=0,sb=0;
for(k=19;k>=0;k--)
if(fa[j][k]+fb[j][k]+s<=x0)
{
s+=fa[j][k]+fb[j][k];
sa+=fa[j][k];
sb+=fb[j][k];
j=g[j][k];
}
if(na[j]!=-1&&s+abs(h[j]-h[na[j]])<=x0)
sa+=abs(h[j]-h[na[j]]);
printf("%lld %lld\n",sa,sb);
}
return 0;
}
【vijos1780】【NOIP2012】开车旅行 倍增的更多相关文章
- P1081 [NOIP2012]开车旅行[倍增]
P1081 开车旅行 题面较为啰嗦.大概概括:一个数列,只能从一个点向后走,两种方案:A.走到和自己差的绝对值次小的点B.走到和自己差的绝对值最小点:花费为此差绝对值:若干询问从规定点向后最多花 ...
- Luogu1081 NOIP2012 开车旅行 倍增
题目传送门 为什么NOIP的题目都这么长qwq 话说2012的D1T3和D2T3都是大火题啊qwq 预处理神题 对于这种跳跳跳的题目考虑使用倍增优化枚举.先预处理某个点之后距离最小和次小的城市,然后倍 ...
- 洛谷1081 (NOIp2012) 开车旅行——倍增预处理
题目:https://www.luogu.org/problemnew/show/P1081 预处理从每个点开始a能走多少.b能走多少.可以像dp一样从后往前推. 但有X的限制.所以该数组可以变成倍增 ...
- Cogs 1264. [NOIP2012] 开车旅行(70分 暴力)
1264. [NOIP2012] 开车旅行 ★★☆ 输入文件:drive.in 输出文件:drive.out 简单对比时间限制:2 s 内存限制:128 MB [题目描述] 小A 和小 ...
- NOIP2012开车旅行 【倍增】
题目 小 A 和小 B 决定利用假期外出旅行,他们将想去的城市从 1 到 N 编号,且编号较小的城市在编号较大的城市的西边,已知各个城市的海拔高度互不相同,记城市 i 的海拔高度为Hi,城市 i 和城 ...
- $Noip2012\ Luogu1081$ 开车旅行 倍增优化$ DP$
Luogu Description Sol 1.发现对于每个城市,小A和小B的选择是固定的,可以预处理出来,分别记为ga[],gb[] 2.并且,只要知道了出发城市和出发天数,那么当前城市和小A,小B ...
- luogu1081 [NOIp2012]开车旅行 (STL::multiset+倍增)
先用不管什么方法求出来从每个点出发,A走到哪.B走到哪(我写了一个很沙雕的STL) 然后把每个点拆成两个点,分别表示A从这里出发和B从这里出发,然后连边是要A连到B.B连到A.边长就是这次走的路径长度 ...
- 【NOIP2012提高组】开车旅行 倍增
题目分析 朴素的做法就是预处理下一个目的地,然后跑模拟,超时. 本题最重要的考点是倍增优化.设$fa[i][j]$表示a从i出发行驶$2^j$“次”后行驶的路程,$fb[i][j]$表示从i出发行驶$ ...
- noip2012开车旅行 题解
题目大意: 给出n个排成一行的城市,每个城市有一个不同的海拔.定义两个城市间的距离等于他们的高度差的绝对值,且绝对值相等的时候海拔低的距离近.有两个人轮流开车,从左往右走.A每次都选最近的,B每次都选 ...
随机推荐
- Windows下的两个缺陷
记事本缺陷: 标题:新建记事本中仅输入“联通”,保存关闭后再打开,显示为乱码 详细描述: 环境说明:操作系统ALL 重现步骤: 1.新建一个记事本,在其中仅输入“联通”两个字 2.再将该记事本关闭保存 ...
- 割顶树 BZOJ1123 BLO
无向图中,求去掉点x[1,n]后每个联通块的大小. 考虑tarjan求bcc的dfs树,对于每个点u及其儿子v,若low[v]<prv[u],则v对u的父亲联通块有贡献,否则对u的子树有贡献.每 ...
- docker 修改port 端口
修改docker容器端口映射的方法 - wesleyflagon的专栏 - CSDN博客https://blog.csdn.net/wesleyflagon/article/details/78961 ...
- MYSQL业务数据简单脱敏方案
removesensitiveinformationplan.sh #!/bin/sh #!在模拟库上运行如下脚本 #!生产库crm-db #!模拟库crm-mock #!.备份生产库 mysqldu ...
- PHP之CLI模式
转载: http://www.cnblogs.com/zcy_soft/archive/2011/12/10/2283437.html 所有的PHP发行版,不论是编译自源代码的版本还是预创建的版本,都 ...
- IdentityServer4【QuickStart】之使用ResourceOwnerPassword流程来保护API
使用ResourceOwnerPassword流程来保护API OAuth2.0中的ResourceOwnerPassword授权流程允许一个客户端发送username和password到token服 ...
- k8s HPA自动收缩
HPA自动收缩 autoscale 自动调整一个 Deployment, ReplicaSet, 或者 ReplicationController 的副本数量 #创建一个replicaset测试 [r ...
- WPF当属性值改变时利用PropertyChanged事件来加载动画
在我们的程序中,有时我们需要当绑定到UI界面上的属性值发生变化从而引起数据更新的时候能够加载一些动画,从而使数据更新的效果更佳绚丽,在我们的程序中尽量将动画作为一种资源放在xaml中,而不是在后台中通 ...
- mac 电脑 打开隐藏文件
command +shift +. 第一次是打开,第二次是关闭
- 利用Python实现“指尖陀螺”,让你释放压力
前言 利用Python实现“指尖陀螺”,让你释放压力 基本环境配置 版本:Python3 系统:Windows 相关模块:turtle 实现效果 不停点击键盘空格键,这个陀螺会慢慢加速,从而达到一个减 ...