洛谷AT2342 Train Service Planning(思维,动态规划,珂朵莉树)

洛谷题目传送门

神仙思维题还是要写点东西才好。

建立数学模型

这种很抽象的东西没有式子描述一下显然是下不了手的。
因为任何位置都以k为周期,所以我们只用关心一个周期,也就是以下数都在膜k意义下。
ai表示i号区间长度;
对于上行列车(0n)设p0表示出发时刻,pi(i1)表示在i站停靠时间;
对于下行列车(0n)设q0表示到站时刻,qi(i1)表示在i站停靠时间;
(转化成q0是为了后面表示方便)
用大写字母A,P,Q分别表示它们的前缀和。
如果某区间bi=1,则两列车的行驶时间区间不交,即

(Pi1+Ai1,Pi1+Ai)(Qi1Ai,Qi1Ai1)=

区间不交即端点不被包含,可以列出不等式(被取模了所以看着比较奇怪)

{Pi1+AiQi1AiPi1+Ai1Qi1Ai1

x=Pi1+Qi1,移项,解得x[2Ai1,2Ai]
因为P,Q是递增的,所以我们的问题变成了:
有若干个限制区间[li,ri],你手头有一个数x,初值任选,每次可以加一个非负整数使它落在区间内,求最少总共加多少能满足限制。

优化求解

考虑这样一个贪心的决策过程:
假设我们知道当前的x,我们需要统计它最少总共被加了多少。我们看上一个限制区间。
如果x被上一个区间包含,那么我们看上上个区间。
如果x没有被上一个区间包含,则x从区间的右端点加过来的代价最小,我们继续对上上个区间的右端点进行决策。
发现我们决策的中间状态只和区间右端点有关,所以我们设fi表示决策到第i个区间时,x=ri的最小代价。
我们对于每个值维护最后一个没有包含这个值的区间编号,每次取出ri对应的编号(记为j),用fj+rirj更新fi,并将[ri+1,li1]的编号全部设为i
最后把所有的li丢进去查一下取个min加上2An就是答案。
因为是区间设置所以可以珂朵莉树维护,不用离散化,因为随机数据的编号段数不会太大所以平均情况下跑得比线段树还快。
特判:如果2ai>k那么puts("-1")

#include<bits/stdc++.h>
#define LL long long
#define R register int
#define G if(++ip==ie)if(fread(ip=buf,1,SZ,stdin))
using namespace std;
const int SZ=1<<19,N=1e5+9;
char buf[SZ],*ie=buf+SZ,*ip=ie-1;
inline int in(){
	G;while(*ip<'-')G;
	R x=*ip&15;G;
	while(*ip>'-'){x*=10;x+=*ip&15;G;}
	return x;
}
int k,l[N],r[N];LL f[N];bool b[N];
struct Node{
	int r;mutable int v;
	inline Node(R a,R b=0):r(a),v(b){}
	inline bool operator<(const Node&a)const{return r<a.r;}
};
typedef set<Node>::iterator IT;
set<Node>s;
inline IT Split(R p){
	IT i=s.lower_bound(Node(p));
	return i->r!=p?s.insert(Node(p,i->v)).first:i;
}
inline void Set(R l,R r,R v){
	if(l>r)return;
	IT il=Split(l-1),ir=Split(r);
	ir->v=v;s.erase(++il,ir);
}
inline LL Calc(R p){
	R j=s.lower_bound(Node(p))->v;
	return j?f[j]+(p-r[j]+k)%k:0;
}
int main(){
	R n=in();LL k=::k=in(),a=0,a1=0,ans=1e18;
	s.insert(Node(-1));
	s.insert(Node(k-1));
	for(R i=1;i<=n;++i){
		a1=a;a+=in();
		if(!(b[i]=in()&1))continue;
		if(2*(a-a1)>k)return puts("-1"),0;
		l[i]=(-2*a1%k+k)%k;r[i]=(-2*a%k+k)%k;
		f[i]=Calc(r[i]);
		if(l[i]>r[i])Set(r[i]+1,l[i]-1,i);
		else Set(0,l[i]-1,i),Set(r[i]+1,k-1,i);
	}
	for(R i=1;i<=n;++i)
		if(b[i])ans=min(ans,Calc(l[i]));
	cout<<ans+2*a<<endl;
	return 0;
}
posted @   Flash_Hu  阅读(572)  评论(2编辑  收藏  举报
编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示
西雅图
17:14发布
西雅图
17:14发布
6°
东南风
3级
空气质量
相对湿度
84%
今天
3°/13°
周六
小雨
6°/15°
周日
中雨
4°/15°