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

JOI2014 バス通学【思维】

题目链接

题目翻译

题面

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

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

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

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

输入

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

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

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

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

输出

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

数据范围

\(2<=N<=100000\)

\(1<=M<=300000\)

\(0<=X_i<Y_i<24*60*60*1000\)

\(1<=Q<=100000\)

\(0<=L_i<24*60*60*1000\)

样例
输入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\)更新\(g\)\(g[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 @ 2020-11-19 08:15  Starlight_Glimmer  阅读(155)  评论(0)    收藏  举报
浏览器标题切换
浏览器标题切换end