【XSY2434】【CF787D】遗产(线段树+dijkstra)

Description

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

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

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

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

1.打开一扇从vu的门

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

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

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

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


Input

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

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


Output

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


Sample Input

样例输入1:

3 5 1
2 3 2 3 17
2 3 2 2 16
2 2 2 3 3
3 3 1 1 12
1 3 3 17

样例输入2:

4 3 1
3 4 1 3 12
2 2 3 4 10
1 2 4 16


Sample Output

样例输出1:

0 28 12

样例输出2:

0 -1 -1 12


Source

练习题 树4-1-线段树


思路

首先,这道题可以很容易看出是一道最短路问题

我们首先想到就是对于v每一个[l,r]区间内的数进行连边

但这样的连边的最坏复杂度都可以达到O(nq),极其容易被卡死

所以我们再仔细观察题目,不去考虑最短路的话。。。

一堆区间操作是不是很像线段树?!

于是我们使用线段树优化最短路

我们考虑对于情况2,3分别建一棵线段树,为入树和出树

其中线段树的每一个节点都表示一个区间

对于操作2,我们就可以将图的节点与入树上区间所对应的节点连边

对于操作3,我们就可以将出树上区间所对应的节点与图的节点连边

我们还需要在入树和出树他们各自自己树内建边,当然权值为0

最后跑一遍最短路dijkstra就可以了


代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=700050,M=3500010l;
const ll INF=0x7f7f7f7f7f7f7f7f;
int n,t,s,cnt=0;
int head[N];
int lc1[N],lc2[N],rc1[N],rc2[N];
//lc1[now],rc1[now]分别表示在入树内now的左右儿子,lc2,rc2是表示在出树内
int to[M<<1],nxt[M<<1];
int val[M<<1];
int nodetot=0;//最短路内的节点数
ll dis[N];
bool vis[N];
struct edge
{
	int u;
	ll cost;
	bool operator < (const edge & e)const
	{
		return cost>e.cost;
	}
}now;
priority_queue<edge>q;
void add(int u,int v,int w)
{
	to[++cnt]=v;
	nxt[cnt]=head[u];
	val[cnt]=w;
	head[u]=cnt;
}
int build1(int l,int r)
{
    if(l==r)return l;
    int now=++nodetot;
    int mid=(l+r)>>1;
    lc1[now-n]=build1(l,mid);
    add(now,lc1[now-n],0);//父亲与儿子连入边,父亲->儿子
    rc1[now-n]=build1(mid+1,r);
    add(now,rc1[now-n],0);
    return now;
}
int build2(int l,int r)
{
    if(l==r)return l;
    int now=++nodetot;
    int mid=(l+r)>>1;
    lc2[now-n]=build2(l,mid);
    add(lc2[now-n],now,0);//父亲与儿子连出边,儿子->父亲
    rc2[now-n]=build2(mid+1,r);
    add(rc2[now-n],now,0);
    return now;
}
void modify1(int v,int l,int r,int w,int ql,int qr,int k)
{
	if(l<=ql&&qr<=r)
	{
		add(v,k,w);//到达区间内,将图内的点与这个区间对应的节点连边
		return ;
	}
	int mid=(ql+qr)>>1;
	if(l<=mid)modify1(v,l,r,w,ql,mid,lc1[k-n]);
        if(r>mid)modify1(v,l,r,w,mid+1,qr,rc1[k-n]);
}
void modify2(int v,int l,int r,int w,int ql,int qr,int k)
{
	if(l<=ql&&qr<=r)
	{
		add(k,v,w);
		return ;
	}
	int mid=(ql+qr)>>1;
	if(l<=mid)modify2(v,l,r,w,ql,mid,lc2[k-n]);
	if(r>mid)modify2(v,l,r,w,mid+1,qr,rc2[k-n]);
}
void dijkstra(int st)
{
	dis[st]=0;
	q.push((edge){st,0});
	while(!q.empty())
	{
		now=q.top();
		q.pop();
		if(vis[now.u])continue;
		vis[now.u]=1;
		dis[now.u]=now.cost;
		for(int i=head[now.u];i;i=nxt[i])
		{
			if(!vis[to[i]]&&(now.cost+(ll)val[i])<dis[to[i]])
			{
				q.push((edge){to[i],now.cost+(ll)val[i]});
			}
		}
	}
}
int main()
{
	scanf("%d %d %d",&n,&t,&s);
	memset(dis,0x7f,sizeof(dis));
	nodetot=n;
	int rt1=build1(1,n);
	int rt2=build2(1,n);
	int op,v,u,l,r,w;
	for(int i=1;i<=t;i++)
	{
		scanf("%d",&op);
		if(op==1)
		{
			scanf("%d %d %d",&v,&u,&w);
			add(v,u,w);//直接连边
		}
		if(op==2)
		{
			scanf("%d %d %d %d",&v,&l,&r,&w);
			modify1(v,l,r,w,1,n,rt1);
		}
		if(op==3)
		{
			scanf("%d %d %d %d",&v,&l,&r,&w);
			modify2(v,l,r,w,1,n,rt2);
		}
	}
	dijkstra(s);
	for(int i=1;i<=n;i++)
	{
		if(dis[i]==INF)printf("-1 ");
		else printf("%lld ",dis[i]);
	}
	return 0;
}
posted @   ShuraEye  阅读(193)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示