首页 > 其他分享 >P9402 [POI2020-2021R3] Droga do domu

P9402 [POI2020-2021R3] Droga do domu

时间:2023-09-01 21:34:28浏览次数:33  
标签:P9402 do POI2020 ch ll le include 换乘 sum

Description

\(n\) 个点, \(m\) 条边,无重边自环,边有长度。

1 号点是学校, \(n\) 号点是家。

\(s\) 条公交线路。公交逢点必停,且一个点不会停两次。在一条边上行驶的时间就是它的长度。给定了第一班 公交发车时间和发车间隔。

在时刻 \(t\) 从学校出发,至多换乘 \(k\) 次,求最早什么时候到家。

只计算路上时间和等车时间。换乘时间不计。

\(2\le n\le 10000,1\le m\le 50000,1\le s\le 25000,0\le k\le 100,0\le t\le 10^9,\sum l\le 50000\)。

Solution

一个图,求起点到终点的最小时间

但是怎么建图。如果直接在原图上面跑,是否换乘了这个问题将难以解决。但是注意到 \(\sum l\le 50000\)。这意味着我们可以对于每条公交线,建 \(\sum l\) 个点,拉成 \(s\) 条链。那么在这个链上转移就不需要换乘,从一条链到另一条链就换乘次数增加 \(1\)。

这样的话,如果我们顺序枚举 \(k\),就可以做到无后效性,从而 \(dp\)。

设 \(f_{i,j}\) 表示到了节点 \(i\),此时换乘了 \(k\) 次的最小时间。注意这里的 \(i\) 的范围是 \(\sum l\)。

那么转移就可以从本链上的点不换乘转移,也可以从别的链上转移过来。

因为是链,所以每个点只会转移到一个点,而且链上节点的顺序是递增的。因此直接枚举 \(i\),用 \(f_{i,j}\) 去更新 \(f_{to_i,j}\)。

至于从别的链上转移,我们可以记录原图中节点 \(i\) 在新图中对应的节点是什么。在这些节点中找到最小的 \(f_{i,j-1}\),去更新 \(f_{i,j}\)。即使 \(f_{x,j}\) 被 \(f_{x,j-1}\) 更新也没有关系,因为换乘次数少的不会劣。

注意更新的顺序。要先更新了换乘的,再去更新不换乘的。这与坐车的顺序是一样的,先选择线路,再做到下一站。

时间复杂度 \(\mathcal O(k\sum l)\)。

Code

#include<queue>
#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
#define N 10005
#define M 50005
#define K 105
#define ll long long
using namespace std;
int n,m,s,k,cnt,rt[M];
ll t,inf,ans,f[M][K];
struct edg {int to;ll val;};
struct node 
{
	int to;
	ll len;
	ll st,ti;
}a[M];
struct que{int x,k;ll t;bool bj;};
vector<edg> g[N];
vector<int> p[N];
queue<que> q;
ll read()
{
    ll res=0;char ch=getchar();
    while (ch<'0'||ch>'9') ch=getchar();
    while (ch>='0'&&ch<='9') res=(res<<1)+(res<<3)+(ch-'0'),ch=getchar();
    return res;
}
void add(int x,int y,ll z,ll st,ll ti) {a[x].to=y;a[x].len=z;a[x].st=st;a[x].ti=ti;}
bool cmp(edg x,edg y) {return x.to<y.to;}
ll calc(ll nowt,ll st,ll ti)
{
    if (nowt<=st) return st;
    ll tn=(nowt-st)/ti;
    if ((nowt-st)%ti!=0) tn++;
    return tn*ti+st;
}
int main()
{
    n=read();m=read();s=read();k=read();t=read();
	for (int i=1;i<=m;++i)
	{
		int x,y;ll z;
        x=read();y=read();z=read();
		g[x].push_back((edg){y,z});
		g[y].push_back((edg){x,z});
	}
    for (int i=1;i<=n;++i)
        sort(g[i].begin(),g[i].end(),cmp);
	for (int i=1;i<=s;++i)
	{
		int num=read();ll x=read(),y=read();
		int lst=0,u=0;
		for (int j=1;j<=num;++j)
		{
			int v;v=read();
			++cnt;
			p[v].push_back(cnt);rt[cnt]=v;
			if (!u) u=v,lst=cnt;
			else
			{
				int l=0,r=g[u].size()-1,mid,res=0;
				while (l<=r)
				{
					mid=(l+r)>>1;
					if (v<=g[u][mid].to) res=mid,r=mid-1;
					else l=mid+1;
				}
				add(lst,cnt,g[u][res].val,x,y);
				lst=cnt;x+=g[u][res].val;u=v;//找到链上每条边的权值
			}
		}
	}
    memset(f,127,sizeof(f));
    inf=f[0][0];
    for (int i=0;i<p[1].size();++i)
        f[p[1][i]][0]=t;
    for (int x=1;x<=cnt;++x)
    {
        int y=a[x].to;
        if (!y) continue;
        ll nxt=calc(f[x][0],a[x].st,a[x].ti)+a[x].len;
        f[y][0]=min(f[y][0],nxt);
    }
    for (int j=1;j<=k;++j)
    {
        for (int now=1;now<=n;++now)
        {
            ll mn=inf;
            for (int i=0;i<p[now].size();++i)
                mn=min(mn,f[p[now][i]][j-1]);
            for (int i=0;i<p[now].size();++i)
                f[p[now][i]][j]=mn;
        }//先换乘
        for (int x=1;x<=cnt;++x)
        {
            int y=a[x].to;
            if (!y) continue;
            ll nxt=calc(f[x][j],a[x].st,a[x].ti)+a[x].len;
            f[y][j]=min(f[y][j],nxt);
        }//再坐到下一站
    }
    ans=inf;
    for (int i=0;i<p[n].size();++i)
        for (int j=0;j<=k;++j)
            ans=min(ans,f[p[n][i]][j]);
    if (ans>=inf) printf("NIE\n");
    else printf("%lld\n",ans);
	return 0;
}

标签:P9402,do,POI2020,ch,ll,le,include,换乘,sum
From: https://www.cnblogs.com/Livingston/p/17672878.html

相关文章

  • Markdown基础语法学习,学习写博客的第一步
    Markdown学习1.标题开头"#"+...:一级标题有n个#表示n级标题2.字体(1)星号:我向往自由,我要谈恋爱!我向往自由,我要谈恋爱!我向往自由,我要谈恋爱!其中"两个星号"+...+"两个星号"表示粗体一个星号表示斜体,三个星号表示斜体加粗体(2)波浪号:我向往自由,我要谈恋爱!我向往自由,......
  • CF1626F A Random Code Problem 题解
    题意给定长度为\(n\)的数组\(a\)和一个整数\(k\),执行下面的代码:longlongans=0;//定义一个初始值为0的长整型变量for(inti=1;i<=k;i++){ intidx=rnd.next(0,n-1);//生成一个介于0到n-1的随机数(含0和n-1) //每个数被选中的概率是相同的 an......
  • JavaScript—DOM
    传统获取方式传统方式元素获取方式<bodyclass="mybody"><inputtype="button"value="点击"id="btn"><divid="dv1"name="mydiv"class="cls"><p>111</p>......
  • 开发小技巧 - 合理使用Visual Studio 2022内置任务列表(TODO)
    前言在开发编码过程中经常会因为各种问题而打断自己的思绪和开发计划,可能会导致本来准备开发或者需要测试的功能到要上线的时候才想起来没有做完。这种情况相信很多同学都遇到过,咱们强大的VisualStudio内置了一个任务列表(TODO)能让我们当做待办清单功能使用,接下来我们快速了解一......
  • Window10 设置 desktop.ini
    通过desktop.ini可以给文件夹和子文件夹自定义一些信息和文件夹图标 1.在文件夹中新建一个txt文档,将文件名和扩展名改为desktop.ini。2.使用notepad++、VisualStudioCode一类的文本编辑器进行编辑。3.相关命令行可查询百度百科[DESKTOP.INI]。4.自定义图标需要IC......
  • Docker深如学习及命令使用
    docker原理及构成:特点:轻量化,易迁移,架构快架构:分层式架构分为:内核、操作系统、上层应用docker使用方式:注:docker创建容器时,必须让容器内有进程在跑着,否则容器会自动挂掉增:获取docker镜像,创建docker容器dockerpullnginx:tagdockerrun-d-p80:90nginx  -d后台运行......
  • how to use R2023a_Doc_Windows.iso
    referencepage:https://www.mathworks.com/help/install/ug/install-documentation-on-offline-machines.htmlmpminstall-doc--matlabroot="C:\ProgramFiles\MATLAB\R2023a"......
  • 游戏服务器成DDoS最大攻击重灾区
    游戏产业的迅猛发展也让游戏产业成为被黑客攻击的重灾区。什么原因让游戏行业成为DDoS的攻击重点。总结有如下原因和主要手段:    1.游戏行业的攻击成本较低,攻防成本1:N。随着DDoS攻击的打法越来越复杂,攻击点更是越来越多,基本的静态防护策略已无法达到较好的效果,易攻难守的特......
  • 使用 Node-RED 构建 DolphinDB 低代码平台
    前言DolphinDB是由浙江智臾科技有限公司研发的一款高性能分布式时序数据库,集成了功能强大的编程语言和高容量高速度的流数据分析系统,为海量结构化数据的快速存储、检索、分析及计算提供一站式解决方案。DolphinDB数据库支持每秒百万级数据写入,万亿级别数据毫秒级查询响应,以及高压......
  • 【转】CountDownLatch的使用
    CountDownLatch使用原理创建CountDownLatch并设置计数器值。启动多线程并且调用CountDownLatch实例的countDown()方法。主线程调用 await() 方法,这样主线程的操作就会在这个方法上阻塞,直到其他线程完成各自的任务,count值为0,停止阻塞,主线程继续执行。使用模板publi......