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;
}