把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

JOI2014 バス通学【思维】

题目链接

题目翻译

题面

大学生JOI君要坐公交车上学。他的家和学校都在IOI市内,他住在1号结点,大学在N号结点。

IOI市有M辆公交车,每辆公交车每天只开一次,从特定的时刻在某个站点出发,在特定的时刻到达某个站点,不能在中途上下车,且时间跨度不会超过一天。

JOI每天要换乘巴士去学校,他换乘的时间可以忽略不计,即:如果要乘坐在timei时刻从posi站点出发的公交车,那么他只需要在timei时刻之前(包含这个时刻)到达站点posi。他可以重复走过一个车站。

JOI要去学校上Q天课,每天上课的时间各不相同,现在他想知道,他每天最晚什么时候从家里出发不会迟到。

输入

第一行包括两个整数N,M

接下来M行,每行4个整数,Ai,Bi,Xi,Yi表示第i辆公交车在时刻Xi从站点Ai出发,在时刻Yi到达Bi。时刻是从午夜零点开始经过了多少毫秒(这个没啥用吧)。

下一行包括一个整数Q,表示上学的天数

接下来Q行,每行一个整数Lj表示第j天必须要在时刻Lj之前到校。

输出

Q行,Q个整数,表示答案,如果不能按时到校,输出1

数据范围

2<=N<=100000

1<=M<=300000

0<=Xi<Yi<2460601000

1<=Q<=100000

0<=Li<2460601000

样例
输入1
5 6
1 2 10 25
1 2 12 30
2 5 26 50
1 5 5 20
1 4 30 40
4 5 50 70
4
10
30
60
100

输出1
-1
5
10
30
输出2
3 8
1 2 1 5
1 3 0 1
1 3 2 8
2 3 2 3
2 3 3 4
2 3 4 5
2 3 5 6
2 3 6 7
6
3
4
5
6
7
8
输出2
0
0
0
1
1
2
小言(基本上是我的碎碎念,可以skip

这是一道布置在某谷上的作业题。

发现某谷上没有翻译,百度没有搜到翻译,我就去把原文翻译过来了。

翻完之后同学告诉我JOI啊呸,我错乱了,是LOJ上有翻译,心态大崩。

只能说练习了日文能力和熟练使用翻译软件的能力?

哼哼,LOJ的翻译就是逐字逐句译出来的而已,没有灵魂(说得好像我自己的翻译就很优秀一样

但是某谷采纳译文之后居然不给署名,气抖冷,原创保护什么时候才能站起来。

题目解析

法一:贪心乱搞

由于我太菜了,不能像其他大佬那样很快地想到递推什么的,于是上来先乱搞。

贪心地想,我们先走到达时间小的边不容易迟到。

而显然L越大,条件越宽松,所以把询问从小到大处理。

由于我们要求的东西是1的时间,这个不方便搜索,所以从n出发。

d[i]表示为了不迟到,从n出发到达i的最晚时间,那么答案就是d[1]

更新答案的时候,沿着边走,记录下到达这个点的最大时间是多少,和这个点公交车的到达时间比较,如果公交车到得比我要求的最大时间还要晚,那就不能走这条边了。

之前把询问从小到大排序,可以发现前面L较小求出来的d[i]可以用来更新L更大时的d[i],你想,之前上学时间早的时候的出发时间肯定不会比上学时间晚的时间更晚,即正确性是有保障的,只是可能不会那么优秀,所以取max就好了。

所以一条边只会经过一次,之前用这条边更新了的答案在d[i]里面,后面可以直接用,不用再走一遍这条边,复杂度是O(M)

法二:dp递推

发现啥时候到校和坐的最后一辆公交车息息相关。

f[i]表示坐第i辆公交车最晚几点出发,g[i]表示到达第i个点最晚几点出发

将所有公交车拆成出发和到达,按时间从小到大排序,f,g相互更新:

出发:用g更新f,相当于统计答案:到校的最晚时间等价于坐上最后那辆公交车的最晚时间。如果这辆公交车的起点是1,那么坐上这辆车的最晚时间就是f[i]=t[i],否则坐上这辆车的最晚时间取决于到达起点的允许最晚出发时间,也就是f[i]=g[u[i]]

到达:用f更新gg[v[i]]=max(g[v[i]],f[i]),如果可以坐上车i,那么在f[i]出发也可以到达g[v[i]]

由于是按照时间从小到大排序,如果大于t[i]g[u[i]]对答案不会产生影响——这个时候g[u[i]]还没有更新到那么大,g[u[i]]要么比t[i]小,要么没有值(无解,因为不可能从起点飞到u[i]来吧)

询问和出发的处理方式是一样的,询问的答案就是对应的f

有个小细节:排序时,如果时间一样,先到达,再出发,因为一样的时间,可以到达之后在马上接着出发。


►Code View Ver.1

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<queue>
using namespace std;
#define LL long long
#define N 100005
#define M 300005
#define DEL 100000
#define INF 0x3f3f3f3f
int rd()
{
	int x=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^48); c=getchar();}
	return f*x;
}
/*
实际上是L时刻从n出发 求到达1的最晚时刻(也就是在路上花费时间最小
把询问从小到大处理 显然L大的边答案会更大 
而且一个更大的L的答案可以用小的L产生的答案更新 所以每条边只会走1次 
d[i]表示从n出发 到达i的最大时刻 
*/ 
int n,m;
int d[N],ans[N];
struct node{
	int v,x,y;
};
vector<node>G[N]; 
struct P{
	int a,b,x,y;
}p[M];
struct Node{
	int id,L;
}query[N];
bool cmp(P l,P r)
{
	return l.y>r.y;
	//先搜到到站时间小的边 到站时间越靠前 越容易合法(不迟到 
	//而我不会删除vector第一个元素 只知道popback qwq 
}
bool cmp2(Node l,Node r)
{
	return l.L<r.L;
}
void dfs(int u,int t)
{//可以到达u的最晚时间为t 
	d[u]=max(d[u],t);
	for(int i=G[u].size()-1;i>=0;i--)
	{
		int v=G[u][i].v;
		if(t<G[u][i].y) return ;//最晚时间都比到达时间早 那肯定会迟到 
		dfs(v,G[u][i].x);
		G[u].pop_back();
	}
}
int main()
{
	n=rd(),m=rd();
	for(int i=1;i<=m;i++)
		p[i].a=rd(),p[i].b=rd(),p[i].x=rd(),p[i].y=rd();
	sort(p+1,p+m+1,cmp);
	for(int i=1;i<=m;i++)
	{
		node t;
		t.v=p[i].a,t.x=p[i].x,t.y=p[i].y;
		G[p[i].b].push_back(t);
	}
	int Q=rd();
	for(int i=1;i<=Q;i++)
		query[i].L=rd(),query[i].id=i;
	sort(query+1,query+Q+1,cmp2);
	d[1]=-1;
	for(int i=1;i<=Q;i++)
	{
		dfs(n,query[i].L);
		ans[query[i].id]=d[1];
	}
	for(int i=1;i<=Q;i++)
		printf("%d\n",ans[i]);
	return 0;
}

►Code View Ver.2

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<queue>
using namespace std;
#define LL long long
#define N 100005
#define M 300005
#define INF 0x3f3f3f3f
int rd()
{
	int x=0,f=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();}
	while(c>='0'&&c<='9'){x=(x<<3)+(x<<1)+(c^48); c=getchar();}
	return f*x;
}
int n,m;
int f[M<<1]/*询问也在这*/,g[N];
struct node{
	int x,t,id,typ;
};
vector<node>query;
bool cmp(node p,node q)
{
	if(p.t==q.t)/*之前这里双等号写成了等号 调好久*/ 
		return p.typ>q.typ;//到达在出发前面 
	return p.t<q.t;
}
int main()
{
	n=rd(),m=rd();
	for(int i=1;i<=m;i++)
	{
		int u=rd(),v=rd(),x=rd(),y=rd();
		node tmp;tmp.x=u,tmp.t=x,tmp.id=i,tmp.typ=0;
		query.push_back(tmp);
		tmp.x=v,tmp.t=y,tmp.typ=1;
		query.push_back(tmp);
	}
	int Q=rd();
	for(int i=1;i<=Q;i++)
	{
		int L=rd();
		node tmp;
		tmp.x=n,tmp.t=L,tmp.id=m+i,tmp.typ=0;
		query.push_back(tmp);
	}
	sort(query.begin(),query.end(),cmp);
	memset(g,-1,sizeof(g));
	for(int i=0;i<query.size();i++)
	{
		int x=query[i].x,id=query[i].id;
		if(query[i].typ==0)
		{//出发 
			if(x==1) f[id]=query[i].t;
			else f[id]=g[x];
		}
		else//到达 
			g[x]=max(g[x],f[id]);
	}
	for(int i=1;i<=Q;i++)
		printf("%d\n",f[m+i]);
	return 0;
} 
posted @   Starlight_Glimmer  阅读(131)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
浏览器标题切换
浏览器标题切换end
点击右上角即可分享
微信分享提示