CF786B Legacy(线段树)(最短路)

Description
【题目描述】

    Rick和他的同事们研究出了一种新的有关放射的公式,于是许多坏人就在追赶他们。所以Rick希望在被坏人抓住之前把遗产给Morty。

    在他们的宇宙里总共有n颗行星,每颗行星有它自己的编号(编号为1到n)。Rick所在的行星的编号是s(地球),但是他不知道Morty在哪?总所周知,Rick有一门能打开奇妙入口的枪。在这把枪的帮助下,他能打开一扇单向门去往任意一个星球(包括那把枪自己所在的星球),但是这玩意是有限制的,因为Rick用的是这玩意的免费试用版。

    一般而言,他不能用这把枪打开任意一扇单向的门。但是有q个套餐在它的官网上售卖。每一次你购买了这个套餐,你就能也仅仅能使用它一次,但是你可以重复购买(如果你觉得需要多次使用的话)。

    网站上的套餐有以下三种类型:

    1.打开一扇从v到u的门

    2.打开一扇从v到[l,r]之间任何一个的门

    3.打开一扇从[l,r]到v之间任何一个的门

    Rick不知道Morty在哪?但是Unity准备告诉他。于是Rick就要准备好一切。

    因为Rick的预算不多,所以他想知道从他的星球出发,到达每一个星球的最少花费是多少,如果到达不了,就输出-1.

【输入描述】

    第一行输入包括三个正整数n,q,s(1<=n,q<=10^5,1<=s<=n),n表示行星的个数,q表示计划的总数,s表示地球的编号。

    接下来q行每行包括一个计划。每一行的第一个数字为t(1<=t<=3),表示该计划的类型。如果t=1就跟着输出v,u和w,w表示这个计划的花费。其他的就输入v,l,r和w。   (1<=v,u<=n,1<=l<=r<=n,1<=w<=10^9)

【输出描述】:

    输出一行包括n个数,ai表示从地球到i这个星球的最小花费,如果到达不了则输出-1。

这题中提到了区间二字,我们自然而然地想到了用线段树来建图,再在图上跑一个最短路就可以了。

那我们应该怎么来建图呢?

首先,(1)的我们只用将单点连边就好了。

那我们怎么处理区间的连边呢?

想象一下,我们用一个线段树来保存区间的连边,比如说,在线段树上在这里插入图片描述
(1-2)就代表1至2这个区间。

但是我们直接在一棵线段树上连边根本不行,入边和出边混在一起,我们以不能分清那一条边是树边还是新添加的边。

所以我们开两棵线段树,分别维护入边和出边。

维护入边的线段树中的全部边朝下,维护出边的线段树中的全部边朝上。

我们再按照题目要求建边,最后再在建好的图上面跑一个dijkstra就好了。

维护入边

void addein(int hao,int l,int r,int L,int R,int x,int cost)
{
	if(L<=l&&R>=r)
	{
		adde(x,in[hao],cost);
		return;
	}
	int mid=(l+r)>>1;
	if(L<=mid)
	{
		addein(hao<<1,l,mid,L,R,x,cost);
	}
	if(R>mid)
	{
		addein(hao<<1|1,mid+1,r,L,R,x,cost);
	}
}

维护出边

void addeout(int hao,int l,int r,int L,int R,int x,int cost)
{
	if(L<=l&&R>=r)
	{
		adde(out[hao],x,cost);
		return;
	}
	int mid=(l+r)>>1;
	if(L<=mid)
	{
		addeout(hao<<1,l,mid,L,R,x,cost);
	}
	if(R>mid)
	{
		addeout(hao<<1|1,mid+1,r,L,R,x,cost);
	}
}

另:cnt一开始要设为 n n n,因为原本就有 n n n个单独的点。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值