【题解】codeforces786B Legacy

题目链接

题意:给定一个n个点m条带权值的“边”的有向图,其中“边”分为3类。第1类“边”是点u到点v的边,第2类“边”是点u到区间[l,r]中的点的边(即r-l+1条边),第3类“边”是区间[l,r]中的点到点u的边。求以点s为源点的最短路。

分析:和atcoder2336类似。考虑给图加一些辅助点和边。对第2类“边”按如下方式处理:对点集[1,n]建立一棵线段树,线段[l,r]维护dis[l],...,dis[r]的上确界。对线段树的每个线段,在图中增加一个结点,设新增结点u、v在线段树中对应父线段与子线段,则连一条从u到v的有向边,权值为0。这么处理之后u到区间[l,r]中的点的边可以分解为O(lgn)条边。对第3类“边”按如下方式处理:对点集[1,n]建立一棵线段树,线段[l,r]维护dis[l],...,dis[r]的下确界。对线段树的每个线段,在图中增加一个结点,设新增结点u、v在线段树中对应父线段与子线段,则连一条从v到u的有向边,权值为0。这么处理之后区间[l,r]中的点到u的边可以分解为O(lgn)条边。建好图后在新图上跑一遍最短路即可。

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn=1e5*40;
struct edge
{
	int u,v;
	LL d;
};
struct node
{
	int u;
	LL d;
	bool operator < (const node &o) const
	{
		return d>o.d;
	}
};
int n,n1,m,s;
LL dis[maxn];
bool done[maxn];
vector<edge> e;
vector<int> G[maxn];
void add(int u,int v,LL d)
{
	e.push_back((edge){u,v,d});
	int k=e.size();
	G[u].push_back(k-1);
}
void build(int o,int l,int r)
{
	n1=max(n1,o);
	if (l==r)
	{
		add(n+o,l,0);
		return ;
	}
	int mid=(l+r)/2;
	add(n+o,n+2*o,0);add(n+o,n+2*o+1,0);
	build(2*o,l,mid);build(2*o+1,mid+1,r);
}
void build2(int o,int l,int r)
{
	if (l==r)
	{
		add(l,n+n1+o,0);
		return ;
	}
	int mid=(l+r)/2;
	add(n+n1+2*o,n+n1+o,0);add(n+n1+2*o+1,n+n1+o,0);
	build2(2*o,l,mid);build2(2*o+1,mid+1,r);
}
void make(int o,int l,int r,int L,int R,int u,LL d)
{
	if (L<=l&&r<=R)
	{
		add(u,n+o,d);
		return ;
	}
	int mid=(l+r)/2;
	if (L<=mid) make(2*o,l,mid,L,R,u,d);
	if (R>mid) make(2*o+1,mid+1,r,L,R,u,d);
}
void make2(int o,int l,int r,int L,int R,int u,LL d)
{
	if (L<=l&&r<=R)
	{
		add(n+n1+o,u,d);
		return ;
	}
	int mid=(l+r)/2;
	if (L<=mid) make2(2*o,l,mid,L,R,u,d);
	if (R>mid) make2(2*o+1,mid+1,r,L,R,u,d);
}
void dijkstra()
{
	priority_queue<node> Q;
	dis[s]=0;
	Q.push((node){s,0});
	while (!Q.empty())
	{
		node nd=Q.top();Q.pop();
		int u=nd.u;
		if (done[u]) continue;
		done[u]=1;
		for (int i=0;i<G[u].size();i++)
		{
			edge e1=e[G[u][i]];
			int v=e1.v;
			if (dis[v]==-1||dis[v]>dis[u]+e1.d)
			{
				dis[v]=dis[u]+e1.d;
				Q.push((node){v,dis[v]});
			}
		}
		//cout<<u<<" "<<dis[4]<<endl;
	}
}
int main()
{
	memset(dis,-1,sizeof(dis));
	cin>>n>>m>>s;
	build(1,1,n);
	build2(1,1,n);
	for (int i=1;i<=m;i++)
	{
		int t,u;
		LL d;
		scanf("%d%d",&t,&u);
		if (t==1)
		{
			int v;
			scanf("%d%I64d",&v,&d);
			add(u,v,d);
		}
		if (t==2)
		{
			int l,r;
			scanf("%d%d%I64d",&l,&r,&d);
			make(1,1,n,l,r,u,d);
		}
		if (t==3)
		{
			int l,r;
			scanf("%d%d%I64d",&l,&r,&d);
			make2(1,1,n,l,r,u,d);
		}
	}
	dijkstra();
	for (int i=1;i<=n;i++) printf("%I64d ",dis[i]);
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值