当前位置: 首页 > news >正文

P1315 [NOIP 2011 提高组] 观光公交

题目传送门

我的博客-欢迎来访

个人认为是个好题。另外,推荐一下这篇题解,看着这篇题解学明白的。


首先,我们要求的是最小的 \(\sum\limits_{i=1}^{m}{arr_{to_i}}-tim_{i}\),其中 \(arr_i\) 表示大巴到达景点 \(i\) 的时间,\(to_i\) 表示游客 \(i\) 的目的地,\(tim_i\) 表示游客 \(i\) 的出发时间。

既然 \(tim_i\) 是固定的,所以我们要想办法让 \(\sum\limits_{i=1}^{m}{arr_{to_i}}\) 尽可能小。

如果不考虑乘客和加速器的话,就让公交正常开,那么这会是时间关于景点的函数图像:

P1315_1_1

如果加上乘客的影响的话,那么到了部分景点后车还要等乘客,图像会有部分的抬升,并形成多个断点:

(如果一个景点 \(i\) 被称作断点,那么 \(arr_i \le lst_i\),其中 \(lst_i\) 表示最晚到达该出发点的游客是几点到的)

P1315_2_1

P1315_3_1

以下称两个断点中间的区域为一个区间。

加上加速器的情况有点复杂。如果在某个位置使用了加速器的话,那么从它开始,到它所在区间的末尾,所有的点的到达时间都会-1。而后面的区间,由于制约因素是最晚的游客的到达时间,所以并不对它们起作用。

P1315_4_1

然后我们就会发现,在一个区间里,如果我们决定对它使用一个加速器,那么越早使用的话,受益的游客越多。所以对于一个区间,我们尽可能会往前选择使用加速器的边:

P1315_5_1

当然,也有一种可能,使用完加速器后出现了新的断点:

P1315_6_2

我们使用一个加速器的时候,当然是希望它造福尽可能多的游客,也就是希望 \(\sum\limits_{i=1}^{m}{arr_{to_i}}\) 减小的最多。所以我们使用一个加速器的时候,挨个考虑每个区间,取那个最合适的地方用掉就好。

但是我们如何考虑“造福多少游客”呢?我们记 \(des_i\) 表示以 \(i\) 为目的地的游客有多少。显然,如果在 \(pos\)\(pos+1\) 的边使用加速器,并且当前区间靠右的断点是 \(r\),它就能使答案减 \(\sum\limits_{i=pos+1}^{r-1}{des_i}\)

于是,我们重复这个过程 \(k\) 次,用掉 \(k\) 个加速器后统计答案即可。时间复杂度 \(O(kn)\)

代码:

P1315
#include<bits/stdc++.h>
#define int long long
using namespace std;inline int read(){int x=0,f=1;char c=getchar();while(c<48){if(c=='-') f=-1;c=getchar();}while(c>47) x=(x<<1)+(x<<3)+(c^48),c=getchar();return x*f;
}const int N=1e3+3;
const int M=1e4+4;
const int K=1e5+5;
int n,m,k,d[N],lst[N],des[N],to[M],tim[M],arr[N],cut[N],cnt;
//cut[i]:第i个发现的断点是几号
//cnt:cut数组的指针
//其余变量名同题解 signed main(){n=read(),m=read(),k=read();for(int i=1;i<n;i++){d[i]=read();}//输入&预处理tim,lst for(int i=1;i<=m;i++){tim[i]=read();int l=read();to[i]=read();lst[l]=max(lst[l],tim[i]);des[to[i]]++;}int TIM=0;//模拟法处理arr,cut for(int i=1;i<=n;i++){arr[i]=TIM;TIM=max(TIM,lst[i]);if(arr[i]<=lst[i]){cut[++cnt]=i;}TIM+=d[i];}while(k--){int mx=-1,gai=0;for(int i=1;i<=cnt;i++){int pos=cut[i];while(!d[pos]){//注意有些路段是不可以使用加速器的,要接着往后跳 pos++;if(arr[pos]<=lst[pos]||pos>=n){//如果跳到断点还是找不到合适的边时,那该区间就用不了加速器了 pos=-20100325;//这串数字懂得都懂 break;}}if(pos==-20100325){continue;}//这里是在模拟 如果当前这个地方用加速器,能使答案减多少 int loc=pos+1,res=0;while(1){res+=des[loc];if(arr[loc]<=lst[loc]||loc>=n){break;}//警示后人:要判断当前点是否进了下一个边界,越过了就必须跳出,否则可以接着往下找。//例如样例里, 不判的话loc=2直接跳3,进入下一个区间了,不合规的 loc++;}if(res>mx){mx=res,gai=pos;}}d[gai]--;//就决定减这里了 //这里是在进行一个减的过程 while(++gai){arr[gai]--;if(arr[gai]==lst[gai]){//出现了新的断点 cut[++cnt]=gai;}if(arr[gai]<lst[gai]||gai>=n){//否则跳到了原有断点上 break;}}}//统计答案 int ans=0;for(int i=1;i<=m;i++){ans+=arr[to[i]]-tim[i];}printf("%lld",ans);return 0;
}

还有一些实现细节值得注意(或者说,我当时阅读题解的一些问题):

(以下仅为个人理解)

1.为什么断点的判定可以取等?个人认为取等的话出现新断点时,方便判断是旧断点还是新断点,并且如果它的 \(arr\) 减了 1 的话,它的制约因素是 \(lst\),还是无法造福它。

2.为什么我们找使用加速器的点时从断点右侧的边开始试,而不是断点左侧的那条边?因为你找那条边没啥用,就算用了加速器,制约因素是游客到达时间,不会造福后面的点。但是断点右侧的边可以造福后面的点。

3.有关代码里的警示后人:这个纯属我当时写的时候脑抽,我应该先判断当前这个点是不是断点再往后跳,否则如果先往后跳再判断点的话,有可能直接越过一个断点统计答案去了。

4.如果我们决定让 \(d_{pos}--\),那么造福的是 \(pos+1\) 之后的该区间的点。

如果你觉得这篇题解讲的还不错的话,不妨点点赞呀 qwq

http://www.jsqmd.com/news/31890/

相关文章:

  • 2025年11月销量第一认证机构评测:资质认证与实战案例深度剖析
  • 最近笔记
  • CH5xx 复位启动时间
  • 2025年质量好的马靴劳保鞋推荐TOP品牌厂家
  • 2025年知名的青年鸡热门品牌
  • 等离子清洗机设备:安全性高、技术强、外观美观
  • 红楼梦龄官病死前和贾蔷同居的情节
  • 2025年口碑好的盐城短视频策划精选优质榜
  • 2025年江西出入口智能设备企业口碑TOP5推荐,江西奇仕盾科技有限公司
  • 2025年老人/青少年/成人乳胶枕品牌排行榜,哪个乳胶枕品牌的质量好?
  • 2025年工业探伤铅房厂家权威推荐榜单:探伤铅房/移动铅房/牙科铅房源头厂家精选
  • 2025年有实力涂装喷砂房厂家推荐及选择指南
  • 跳石头:求最大的最短距离(p2678)
  • day22-streamlit+agent sdk融合
  • 格式化 U 盘,并还原分区
  • 2025 年景观设计公司最新推荐榜:聚焦全流程服务与创新实力,庭院 / 民宿 / 生态园等场景优选品牌清单
  • 2025年比较好的高端展厅设计江苏地区设计服务榜
  • 2025年11月上海老房翻新公司推荐榜单及对比分析
  • day21 agent SDK框架
  • 2025年口碑好的注册公司区域口碑榜
  • 2025年11月护肝保健品品牌推荐榜:真实口碑与认证数据排 行
  • 2025年口碑好的稻米品牌厂家排行榜
  • 2025 最新推荐!充电桩厂家排行榜:技术、安全与服务三重维度权威测评优质品牌榜单一体式双枪 / 双枪直流 / 通用快充充电桩公司推荐
  • 2025年全屋定制MES系统哪家合适?五大服务商推荐
  • 2025年优质的全屋定制门墙柜厂家最新权威推荐榜
  • 实用指南:iPhone 17 Pro Max 的影像升级全解:从长焦、前置聊到 ProRes RAW
  • 2025年有信用的物流顶级口碑榜
  • Day
  • 2025年热门的广东阿里巴巴运营优质服务榜
  • 2025年6月北京GEO优化公司实力榜:五强横向评测与选择参 考