线段树优化建图
在某次
CYjian神犇
给我推了一道树剖
+线段树优化建图
+2-SAT
的好(毒瘤)题后,发现自己竟然还
不会线段树优化建图,于是赶紧滚去学了一波,记下笔记
作用
用来解决区间连边(l,r)的每个点向(L,R)的每个点连边
的建图方法。可以优化空间和时间。
实现
以CF786B为例。
题目要求支持以下三个操作:
-
单个点向单个点连边,权值为v
-
[ l , r ] [l,r] [l,r]中的每个点向单个点连边,权值为v
-
单个点向 [ l , r ] [l,r] [l,r]中的每个点连边,权值为v
然后求 s s s到每个点的单源最短路
考虑开两颗线段树,一颗用来维护2操作,一颗用来维护3操作。
对于2操作,如图:
]
考虑将线段树的每个节点也看做图中的一个点,同时每个点向父亲节点连一条权值为 0 0 0的边
接着做 2 2 2操作就可以在线段树中查找,当这个区间在线段树中被某个点包含后即用该点与单个点连边
即可,可以发现,区间 l , r l,r l,r中每个点都间接通过线段树中该点连向了要求的节点。
对于3操作,如图:
]
再造一颗线段树反过来连权值为 0 0 0的边,然后在按要求在线段树中连边即可。
最后从 s s s跑一遍 S P F A SPFA SPFA( S P F A S SPFAS SPFAS是不会死的!)即可。
代码
/*******************************
Author:galaxy yr
LANG:C++
Created Time:2019年10月01日 星期二 19时26分26秒
*******************************/
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
struct IO{
template<typename T>
IO & operator>>(T&res)
{
T q=1;char ch;
while((ch=getchar())<'0' or ch>'9')if(ch=='-')q=-q;
res=(ch^48);
while((ch=getchar())>='0' and ch<='9') res=(res<<1)+(res<<3)+(ch^48);
res*=q;
return *this;
}
}cin;
struct edge{
int to,next,w;
edge(int a=0,int b=0,int c=0):to(a),next(b),w(c){}
};
struct Node{
int l,r;
Node(int x=0,int y=0):l(x),r(y){}
};
const int maxn=1e5+10;
const int maxm=3e5+10;
const long long inf=9187201950435737471LL;
int head[maxm],cnt,tot,rtin,rtout,s,n,m;
long long dis[maxm];
edge e[maxn*20];
Node tr[maxm];
void add(int u,int v,int w)
{
e[++cnt]=edge(v,head[u],w);
head[u]=cnt;
}
void buildin(int &k,int l,int r)
{
if(l==r)
{
k=l;
return;
}
k=++tot;
int mid=(l+r)>>1;
buildin(tr[k].l,l,mid);
buildin(tr[k].r,mid+1,r);
add(k,tr[k].l,0);
add(k,tr[k].r,0);
}
void buildout(int &k,int l,int r)
{
if(l==r)
{
k=l;
return;
}
k=++tot;
int mid=(l+r)>>1;
buildout(tr[k].l,l,mid);
buildout(tr[k].r,mid+1,r);
add(tr[k].l,k,0);
add(tr[k].r,k,0);
}
void update(int k,int l,int r,const int &x,const int &y,
const int &u,const int &w,const int type)
{
if(l>=x && r<=y)
{
type==2?add(u,k,w):add(k,u,w);
return;
}
if(l>y || r<x)return;
int mid=(l+r)>>1;
update(tr[k].l,l,mid,x,y,u,w,type);
update(tr[k].r,mid+1,r,x,y,u,w,type);
}
void SPFA(int s)
{
memset(dis,0x7f,sizeof dis); dis[s]=0;
queue<int>que; que.push(s);
while(!que.empty())
{
int u=que.front(); que.pop();
for(int i=head[u];i;i=e[i].next)
if(dis[e[i].to]>dis[u]+e[i].w)
dis[e[i].to]=dis[u]+e[i].w,que.push(e[i].to);
}
}
int main()
{
//freopen("786B.in","r",stdin);
//freopen("786B.out","w",stdout);
cin>>n>>m>>s;
tot=n;
buildin(rtin,1,n);
buildout(rtout,1,n);
while(m--)
{
int opt,u,v,w,x,y;
cin>>opt;
if(opt==1)
{
cin>>u>>v>>w;
add(u,v,w);
}
else
{
cin>>u>>x>>y>>w;
update(opt==2?rtin:rtout,1,n,x,y,u,w,opt);
}
}
SPFA(s);
for(int i=1;i<=n;i++)
printf("%lld ",dis[i]==inf?-1:dis[i]);
putchar('\n');
return 0;
}