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;
}
# P14325 [JOI2022 预选赛 R2] 图书馆 2 / Library 2 ## 题目描述 热爱读书的比太郎决定从图书馆借书来阅读。由于比太郎的家空间狭小,床边仅能容纳一本书的宽度,但高度足够,因此他决定将书堆叠在该空间内进行管理。 比太郎将执行 $ Q $ 次操作。第 $ i $($ 1 \le i \le Q $)次操作由字符串 $ S_i $ 表示。$ S_i $ 要么是由小写英文字母组成的字符串,要么是字符串 READ,其含义如下: - 若 $ S_i $ 是由小写英文字母组成的字符串,比太郎将从图书馆借阅书名为 $ S_i $ 的书,并将其堆叠在空间最上方。 - 若 $ S_i $ 是 READ,比太郎将阅读当前堆叠在空间最上方的书,然后将其归还图书馆。 你需要调查比太郎阅读书籍的顺序。 当给出 $ Q $ 次操作的内容时,请编写一个程序,按比太郎阅读书籍的顺序输出所读书籍的书名。 ## 输入格式 输入通过标准输入以如下格式给出: $ Q $ $ S_1 $ $ S_2 $ $ \vdots $ $ S_Q $ ## 输出格式 在标准输出中,对于每个满足 $ S_i $ 为 READ 的操作,按顺序逐行输出比太郎所读书籍的书名。 ## 输入输出样例 #1 ### 输入 #1 ``` 7 joi joig ioi READ egoi READ READ ``` ### 输出 #1 ``` ioi egoi joig ``` ## 输入输出样例 #2 ### 输入 #2 ``` 20 one READ two three four five six seven READ eight nine READ ten eleven READ READ twelve READ READ READ ``` ### 输出 #2 ``` one seven nine eleven ten twelve eight six ``` ## 说明/提示 ### 样例 1 解释 在此输入样例中,比太郎将按以下方式行动: 1. 将书名为 $ joi $ 的书堆叠到空间中。此时,空间中堆叠的书的书名为 $ joi $。 2. 将书名为 $ joig $ 的书堆叠到空间中。此时,空间中堆叠的书的书名从上至下依次为 $ joig $、$ joi $。 3. 将书名为 $ ioi $ 的书堆叠到空间中。此时,空间中堆叠的书的书名从上至下依次为 $ ioi $、$ joig $、$ joi $。 4. 阅读并归还书名为 $ ioi $ 的书。此时,空间中堆叠的书的书名从上至下依次为 $ joig $、$ joi $。 5. 将书名为 $ egoi $ 的书堆叠到空间中。此时,空间中堆叠的书的书名从上至下依次为 $ egoi $、$ joig $、$ joi $。 6. 阅读并归还书名为 $ egoi $ 的书。此时,空间中堆叠的书的书名从上至下依次为 $ joig $、$ joi $。 7. 阅读并归还书名为 $ joig $ 的书。此时,空间中堆叠的书的书名为 $ joi $。 因此,比太郎所读书籍的书名按顺序为 $ ioi $、$ egoi $、$ joig $,请逐行输出。 此输入样例满足所有子任务的约束。 ### 数据范围 - $ 2 \le Q \le 200\,000 $。 - $ Q $ 为整数。 - $ S_i $ 是长度在 $ 1 $ 以上、$ 10 $ 以下的字符串($ 1 \le i \le Q $)。 - $ S_i $ 为由小写英文字母组成的字符串,或为 READ($ 1 \le i \le Q $)。 - 存在至少一个 $ i $($ 1 \le i \le Q $),使得 $ S_i $ 为 READ。 - 当 $ S_i $ 为 READ 时,空间中必定至少存在一本书($ 1 \le i \le Q $)。 ### 子任务 1. (40 分)$ Q \le 2\,000 $。 2. (60 分)无额外制约。 这道题用#include<bits/stdc++.h> using namespace std; int main() { int q; string s; cin>>q; stack<string>a; for(int i=1;i<=q;i++) { cin>>s; a.push(s); } for(int i=1;i<=q;i++) { //if(a.top()=="READ") //{ //for(int j=1;j<=a.size();j++) //{ //a.pop(); if(a.top()=="READ") { a.pop(); } else { cout<<a.top()<<endl; a.pop(); } //} //} } return 0; }做不对
10-29
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值