CF786B 线段树优化建图

题目大意

\(n\)个星球,编号\(1-n\),可以建立下面三种边(q次):

  • 星球\(u\)到编号\(l-r\)间的星球每一个建立一条边
  • 编号\(l-r\)间的每一个星球到星球\(u\)建立一条边
  • 星球\(u\)到星球\(v\)建立一条边
    给定起点,问起点到其他个点的最短距离。
    \(n,q \leq 1e5\)

求最短路并不难,问题在于边数过多。
由于建立的边都是从某个点到一定的区间范围内的点,可以考虑给区间家里一个点,这样就减少了边的数量。这个就是线段树优化建边。
方法:

  • 给区间\(1-n\)建立两颗线段树。一棵树叫出树,另一棵叫入树。出树内建立从父节点到左右子节点的有向边。入树内建立从左右孩子到父节点的有向边。
  • 用无向边把出树和入树的叶节点按编号对应连起来。
  • 剩余要建的边;(1)如果是单点相连,直接对入树中对应的点到出树中对应的点建边(2)如果是区间建边,则应用线段树把区间分解到对应的线段树中的点,从入树到出树建边。

有些代码中把入树和出树的叶子节点设为同一点,这样就不用建立无向边了。
注意建图后图中的点数和边数!!!


代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
int n,qq,s;
int cnt,lc[maxn*3],rc[maxn*3];
int rootin,rootout;

struct edge
{
	int u,v,w,nxt;
}e[maxn*20];
int head[maxn*3],js;
void addage(int u,int v,int w)
{
	e[++js].v=v;e[js].u=u;e[js].w=w;
	e[js].nxt=head[u];head[u]=js;
}

void buildin(int &cur,int l,int r)
{
	if(l==r)
	{
		cur=l;
		return ;
	}
	cur=++cnt;
	int mid=(l+r)>>1;
	buildin(lc[cur],l,mid);
	buildin(rc[cur],mid+1,r);
	addage(lc[cur],cur,0);
	addage(rc[cur],cur,0);
}
void buildout(int &cur,int l,int r)
{
	if(l==r)
	{
		cur=l;
		return ;
	}
	cur=++cnt;
	int mid=(l+r)>>1;
	buildout(lc[cur],l,mid);
	buildout(rc[cur],mid+1,r);
	addage(cur,lc[cur],0);
	addage(cur,rc[cur],0);
}
void updat(int root,int l,int r,int ql,int qr,int u,int w,int opt)
{
	if(ql<=l&&r<=qr)
	{
		if(opt==2)addage(u,root,w);
		else addage(root,u,w);
		return ;
	}
	int mid=(l+r)>>1;
	if(ql<=mid)updat(lc[root],l,mid,ql,qr,u,w,opt);
	if(mid<qr)updat(rc[root],mid+1,r,ql,qr,u,w,opt); 
}
long long dis[maxn*3];
queue<int>q;
bool inq[maxn*3]; 
void spfa(int s)
{
	memset(dis,0x3f,sizeof dis);
	q.push(s);inq[s]=1;dis[s]=0;
	while(!q.empty())
	{
		int u=q.front();
		q.pop();inq[u]=0;
		for(int i=head[u];i;i=e[i].nxt)
		{
			int v=e[i].v;
			if(dis[v]>dis[u]+e[i].w)
			{
				dis[v]=dis[u]+e[i].w;
				if(!inq[v])
				{
					q.push(v);
					inq[v]=1;
				}
			}
		}
	}
}
int main()
{
	scanf("%d%d%d",&n,&qq,&s);
	cnt=n;
	buildin(rootin,1,n);
	buildout(rootout,1,n);
	while(qq--)
	{
		int opt,u,l,r,w;
		scanf("%d",&opt);
		if(opt==1)
		{
			scanf("%d%d%d",&u,&l,&w);
			addage(u,l,w);
		}
		else 
		{
			scanf("%d%d%d%d",&u,&l,&r,&w);
			updat(opt==2?rootout:rootin,1,n,l,r,u,w,opt);
		}
	}
	spfa(s);
	for(int i=1;i<=n;++i)printf("%lld ",dis[i]==0x3f3f3f3f3f3f3f3f?-1:dis[i]);
	return 0;
}

posted on 2022-05-13 11:08  gryzy  阅读(48)  评论(0编辑  收藏  举报

导航