EGOI2021 Lanterns / 灯笼

2023-10-27 23:45
文章标签 灯笼 egoi2021 lanterns

本文主要是介绍EGOI2021 Lanterns / 灯笼,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

洛谷P9312 [EGOI2021] Lanterns / 灯笼

题目大意

n n n座山脉,这些山脉可以用平面直角坐标系上的 n n n个点表示,我们称这些点为山峰。第 i i i座山峰的坐标为 ( i , h i ) (i,h_i) (i,hi) h i h_i hi表示第 i i i座山峰的海拔高度,保证 h 1 , h 2 , … , h n h_1,h_2,\dots,h_n h1,h2,,hn构成一个 1 ∼ n 1\sim n 1n的排列。

山峰 i i i和山峰 i + 1 i+1 i+1用一条线段相连。

约翰要携带至少一盏正常工作的灯笼才能上山。有 k k k盏灯笼可以购买,第 j j j盏灯笼可以在山峰 p j p_j pj上以 c j c_j cj法郎的价格购买。

然而,第 j j j盏灯笼只有约翰的海拔在 [ a j , b j ] [a_j,b_j] [aj,bj]时才能正常工作,否则就会停止工作,在回到对应海拔上才会恢复正常工作。

如果现在约翰在山峰 p p p,他可以执行以下三种操作之一:

  • 购买一个山峰 p p p上售卖的灯笼
  • 如果 p > 1 p>1 p>1,他可以走到山峰 p − 1 p-1 p1
  • 如果 p < n p<n p<n,他可以走到山峰 p + 1 p+1 p+1

约翰在没有正常工作的灯笼时不能移动。他必须保证每个时刻都有至少一盏灯笼正常工作,才能在两座山峰间移动。(在行走过程中不必是同一盏灯笼。)

对于每一个 1 ≤ p ≤ n 1\leq p\leq n 1pn,求约翰一开始在山峰 p p p上时,他至少要花费多少法郎的价格才能走遍所有山脉。

1 ≤ n , k ≤ 2 × 1 0 3 1\leq n,k\leq 2\times 10^3 1n,k2×103

时间限制 3000 m s 3000ms 3000ms,空间限制 1024 M B 1024MB 1024MB


题解

首先,我们知道,约翰购买的灯笼要是连续的,因为如果不连续的话,就会有一些地方走不到。如果这些地方不需要走的话,就不是最优的。那么,我们只需要记录所有所有已购买的灯笼中最小的 a a a a i a_i ai和最大的 b b b b j b_j bj,即可知道当前所有已购买的灯笼构成的区间。

f l , r , v f_{l,r,v} fl,r,v表示当前在第 v v v座山峰,灯笼构成的区间为 [ l , r ] [l,r] [l,r],继续探索完所有山峰至少需要花费的价格。因为能够构成一个区间 [ l , r ] [l,r] [l,r]的横坐标有多段,所以要记录一个 v v v来表示当前在那一段。

我们发现, f l , r , v f_{l,r,v} fl,r,v中的 l l l一定是当前购买的灯笼中 a a a值最小的, r r r一定是当前购买的灯笼中 b b b值最大的。设 a a a值最小的为 a i a_i ai b b b值最小的为 b j b_j bj,那么如果用 i i i j j j来表示一个状态,那么构成 [ a i , b j ] [a_i,b_j] [ai,bj]的横坐标段一定经过 p i p_i pi p j p_j pj,这样就能确定其所在的横坐标段。所以我们不在需要记录当前所在的山峰 v v v,只需要记录状态 f i , j f_{i,j} fi,j即可。

那么,转移式为

f i , j ← min ⁡ r j ≤ r t { f i , t + w t } f i , j ← min ⁡ l t < l i { f t , j + w t } f i , j ← min ⁡ l t < l i , r j < r t { f t , t + w t } \begin{aligned} f_{i,j}&\leftarrow \min\limits_{r_j\leq r_t}\{f_{i,t}+w_t\} \\ f_{i,j}&\leftarrow \min\limits_{l_t<l_i}\{f_{t,j}+w_t\} \\ f_{i,j}&\leftarrow \min\limits_{l_t<l_i,r_j<r_t}\{f_{t,t}+w_t\} \end{aligned} fi,jfi,jfi,jrjrtmin{fi,t+wt}lt<limin{ft,j+wt}lt<li,rj<rtmin{ft,t+wt}

其中, t t t需要满足 l i ≤ t ≤ r j l_i\leq t\leq r_j litrj

在枚举 i , j i,j i,j的时候,我们要按 l i l_i li从小到大枚举 i i i,按 r j r_j rj从大到小枚举 j j j

这样转移的时间复杂度是 O ( n 3 ) O(n^3) O(n3)的,我们考虑优化。

对于第一种转移,我们注意到:当 r i < r j < r t r_i<r_j<r_t ri<rj<rt,且 t t t不能被 f h , j f_{h,j} fh,j购买,则 t t t不能被 f h , i f_{h,i} fh,i购买。那么,我们可以维护 k k k个小根堆,第 i i i个小根堆 q l i ql_i qli存储 f i , t + w t f_{i,t}+w_t fi,t+wt。在查询的时候,如果堆顶的 p t p_t pt无法到达,则将其弹出,否则就用其更新当前的 f f f值。

对于第二种转移,与第一种转移类似,用 k k k个小根堆,第 j j j个小根堆 q r j qr_j qrj存储 f t , j + w t f_{t,j}+w_t ft,j+wt,和上面一样处理即可。

对于第三种转移, f t , t → f i , j f_{t,t} \rightarrow f_{i,j} ft,tfi,j的过程可以看作用上面两种转移来描述: f t , t → f t , j → f i , j f_{t,t} \rightarrow f_{t,j} \rightarrow f_{i,j} ft,tft,jfi,j。不过, f t , j f_{t,j} ft,j是不合法状态( r t > r j r_t>r_j rt>rj),于是我们定义这样的 f t , j f_{t,j} ft,j为合法状态,并且其值为 f t , t f_{t,t} ft,t,这样我们就能让这种转移在前两种转移中体现。

时间复杂度为 O ( k 2 log ⁡ k ) O(k^2\log k) O(k2logk)

code

#include<bits/stdc++.h>
using namespace std;
const int N=2000,inf=2100000000;
int n,k,h[N+5],p[N+5],w[N+5],a[N+5],b[N+5];
int dpl[N+5],dpr[N+5],vl[N+5][2],vr[N+5][2],f[N+5][N+5];
bool cmp1(int x,int y){return a[x]<a[y];}
bool cmp2(int x,int y){return b[x]>b[y];}
struct node{int x,id;friend bool operator<(node ax,node bx){return ax.x>bx.x;}
};
priority_queue<node>ql[N+5],qr[N+5];
int gtval(priority_queue<node>&q,int l,int r,int hl,int hr){while(!q.empty()){int tp=q.top().id;if(p[tp]<l||p[tp]>r||b[tp]<hl||a[tp]>hr) q.pop();else return q.top().x;}return inf;
}
int main()
{scanf("%d%d",&n,&k);for(int i=1;i<=n;i++) scanf("%d",&h[i]);for(int i=1;i<=k;i++){scanf("%d%d%d%d",&p[i],&w[i],&a[i],&b[i]);dpl[i]=dpr[i]=i;}sort(dpl+1,dpl+k+1,cmp1);sort(dpr+1,dpr+k+1,cmp2);for(int i=1;i<=k;i++){for(int &j=vl[i][0]=p[i]+1;j-1>=1&&h[j-1]>=a[i];--j);for(int &j=vl[i][1]=p[i]-1;j+1<=n&&h[j+1]>=a[i];++j);for(int &j=vr[i][0]=p[i]+1;j-1>=1&&h[j-1]<=b[i];--j);for(int &j=vr[i][1]=p[i]-1;j+1<=n&&h[j+1]<=b[i];++j);}for(int w1=1;w1<=k;w1++){for(int w2=1;w2<=k;w2++){int i=dpl[w1],j=dpr[w2];f[i][j]=inf;int l=max(vl[i][0],vr[j][0]),r=min(vl[i][1],vr[j][1]);if(p[i]<l||p[i]>r||p[j]<l||p[j]>r||(a[j]<a[i]&&b[j]<b[i])) continue;if(l==1&&r==n) f[i][j]=0;else if(a[j]<a[i]) f[i][j]=f[j][j];else if(b[j]<b[i]) f[i][j]=f[i][i];else{f[i][j]=min(f[i][j],gtval(ql[i],l,r,a[i],b[j]));f[i][j]=min(f[i][j],gtval(qr[j],l,r,a[i],b[j]));}ql[i].push((node){f[i][j]+w[j],j});qr[j].push((node){f[i][j]+w[i],i});}}for(int i=1;i<=k;i++){if(f[i][i]<inf) printf("%lld\n",f[i][i]+w[i]);else printf("-1\n");}return 0;
}

这篇关于EGOI2021 Lanterns / 灯笼的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/289235

相关文章

01背包【B站正月点灯笼讲解版和yxc版】

yxc版 题目链接 从1开始,f[i][j]表示前i个物品占用j空间的最大价值。 v表示第i件物品的体积,p表示第i件物品的价值。 f[i][j]可以有两个转移来源。 不选第i个物品:f[i][j] = f[i-1][j]选第i个物品:f[i][j] = f[i-1][j - v[i]] + p[i] 时间复杂度 O ( n m ) O(nm) O(nm) #include <iostre

用HTML5实现灯笼效果

本文介绍了两种实现效果:一种使用画布(canvas)标签/元素,另一种不用画布(canvas)标签/元素主要使用CSS实现。 使用画布(canvas)标签/元素实现,下面,在画布上,用JavaScript画两个红灯笼,并且红灯笼左右来回移动的源码: <!DOCTYPE html><html><head><title>红灯笼</title><style>canvas {border: 1px

Cocos 水友自创微信小游戏 - 元宵灯笼连连看

元宵一过,这个新年就算接近尾声了。17 年末刚发布的微信小游戏基于过亿用户的庞大基础,热潮依然持续发酵。越来越多的开发者发现:「开发一个小游戏才是正经事」!简书水友 ID 汀江秋雨结合元宵的节日气氛,基于 Cocos Creator v1.8.1 制作了个喜庆版的元宵灯笼连连看,还在纠结怎么开发微信小游戏的新手们,一起来看看吧。 原链接:https://www.jianshu.com/p/740

【教学类-39】A4红纸-国旗灯笼纸模(庆祝中华人民共和国成立74周年)

作品展示: 背景需求: 从教十余年,我在每年国庆都带领中大班孩子们制作与“国旗相关”国庆庆祝物品——国旗、礼盒 一、国旗(吸管、A4红纸、黄纸打印五角星) 二、铅画纸手提袋(8K铅画纸、A4红纸、黄色打印星星) 而在春节期间,就经常做这样的简易灯笼 一、A4红卡纸灯笼 二、8K染色铅画纸灯笼 每次做这种一次性节日手工项目,都是一地狼藉、满桌废纸。我疲于奔

“灯笼高高挂”助手

再次献丑。 之前写过一个“穿越福城”助手,感觉是个大失败…参数很难调,稳定性也非常差,不想改进了。然而第二个小游戏好白痴…不打算给它写助手了,就写第三个的吧。 这个“灯笼高高挂”就比较好做了,参数用对了之后,只要不手动停下来,它就永远都不会输。跑到六百多就不想跑了。 主要思想: 先写一个能算周期的程序跑一会儿,能够发现,在最好的情况下(掉下来的房子大小不变),前7个房子的周期都是3s,随后

微信小程序:元宵灯笼连连看小游戏

20180301 by 慕容秋 写在前面 前些天闲聊中跟家里的领导说,微信也可以做小游戏诶。然后她说,那你做个连连看游戏给我玩玩呗。再然后就有了这几天的摸索和下面的一些小结: 演示效果: http://link.muroqiu.com源码地址: https://gitee.com/muroqiu/LinkUp 开发工具: Cocos Creator v1.8.1Visual Studio Co