CF786B Legacy(线段树优化建图,内向树外向树连边)

CF786B Legacy

在这里插入图片描述

题目大意:

要求见一张图:

1、一个点连向另一个点,代价为 w w w

2、一个点连向一个区间,代价为 w w w

3、一个区间连向一个点,代价为 w w w

求连完点后,以以为初始节点,跑最短路,求初始节点到每一个节点的最短路

以上连边均是单向连边

先考虑朴素的算法,就是暴力连边,那么时间复杂度就是 O ( n 2 ) O(n^2) O(n2),直接超时了

这里要通过线段树要优化建图,这里要有一个知识点,内向树与外向树,

内向树如下:

所有线段树的节点都连向区间的内部,而且只允许外部连向内部,如图

在这里插入图片描述

以下的这种是错误的内向树,因为连向了外面

在这里插入图片描述

而外向树却恰恰相反,如图

在这里插入图片描述

而以下是错误的外向树

在这里插入图片描述

可以建两棵树一颗是内向树,另一颗是外向树,在建树的时候就把内向树,外向树的建好,然后就是连边,只要写成区间修改的形式就行了,也就是一个点连向一个区间就行了

当然需要动态开点,但叶子节点要和区间号一致,所以开点的时候就是要从 n + 1 开 始 n+1开始 n+1

连的时候注意下内向树,外向树就行了,另外还要注意 20 20 20倍空间

#pragma GCC optimize(2)
#pragma GCC optimize(3,"Ofast","inline")
#include <bits/stdc++.h>
#define inf 0x7fffffff
#define ll long long
#define int long long
//#define double long double
#define re register int
#define void inline void
#define eps 1e-8
//#define mod 1e9+7
#define ls(p) p<<1
#define rs(p) p<<1|1
#define pi acos(-1.0)
#define pb push_back
#define P pair < int , int >
#define mk make_pair
using namespace std;
const int mod=998244353;
const int M=1e9;
const int N=1e5+5;//?????????? 4e8
struct node
{
	int ver,edge,next;
}e[N*20];
int tot,head[N*8];
int cnt,lc[N*8],rc[N*8];
int n,m,v[N*4]; 
ll d[N*4];
int rt1,rt2;
void add(int x,int y,int z)
{
	e[++cnt].ver=y;
	e[cnt].edge=z;
	e[cnt].next=head[x];
	head[x]=cnt;
}
void bulid1(int &p,int l,int r)
{
	if(l==r)
	{
		p=l;
		return;
	}
	p=++tot;
	int mid=(l+r)>>1;
	bulid1(lc[p],l,mid);bulid1(rc[p],mid+1,r);
	add(p,lc[p],0);add(p,rc[p],0);
}
void bulid2(int &p,int l,int r)
{
	if(l==r)
	{
		p=l;
		return;
	}
	p=++tot;
	int mid=(l+r)>>1;
	bulid2(lc[p],l,mid);bulid2(rc[p],mid+1,r);
	add(lc[p],p,0);add(rc[p],p,0);
}
void update(int p,int L,int R,int l,int r,int x,int z,int op)
{
	if(L<=l&&r<=R)
	{
		if(op==2)  add(x,p,z);
		else  add(p,x,z);
		return;
	}
	int mid=(l+r)>>1;
	if(L<=mid)  update(lc[p],L,R,l,mid,x,z,op);
	if(mid<R)  update(rc[p],L,R,mid+1,r,x,z,op);
}
void dijkstra(int s)
{
	priority_queue < P > q;
	for(re i=0;i<N*4;i++)  d[i]=1e18,v[i]=0;
	d[s]=0;q.push(mk(0,s));
	while(q.size())
	{
		int x=q.top().second;q.pop();
		if(v[x])  continue;
		v[x]=1;
		for(re i=head[x];i;i=e[i].next)
		{
			int y=e[i].ver;
			int z=e[i].edge;
			if(d[y]>d[x]+z)
			{
				d[y]=d[x]+z;
				q.push(mk(-d[y],y));
			}
		}
	}
}
void solve()
{
	int s;
	cin>>n>>m>>s;
	tot=n;
	bulid1(rt1,1,n);bulid2(rt2,1,n);
	while(m--)
	{
		int op,x,l,r,z;
		scanf("%lld",&op);
		if(op==1)
		{
			scanf("%lld%lld%lld",&l,&r,&z);
			add(l,r,z);
		}
		else
		{
			scanf("%lld%lld%lld%lld",&x,&l,&r,&z);
			update(op==2?rt1:rt2,l,r,1,n,x,z,op);
		}
	}
	dijkstra(s);
	for(re i=1;i<=n;i++)  if(d[i]==1e18)  d[i]=-1;
	for(re i=1;i<=n;i++)  printf("%lld ",d[i]);
}
signed main()
{
    int T=1;
//    cin>>T;
    for(int index=1;index<=T;index++)
    {
//        printf("Case %d:\n",index);
        solve();
//        puts("");
    }
    return 0;
}
/*


1
6
URLLDR




*/
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值