前言
百川到海,天下归一
解析
线段树优化建图是用于对一个区间的点连边时的优化方法
建一棵in树一棵出树分别往上和下指即可
大概长这样
(pia的洛谷的照片)
建树
正常动态开点即可
void build(int &k,int l,int r){
tr[k=++tot]=(tree){0,0};
if(l==r){
addline(k,l);
return;
}
build(tr[k].ls,l,mid);build(tr[k].rs,mid+1,r);
addline(k,tr[k].ls);addline(k,tr[k].rs);
//printf("k=%d l=%d r=%d ls=%d rs=%d\n",k,l,r,tr[k].ls,tr[k].rs);
return;
}
连边
和线段树的通常操作很类似
void add(int k,int l,int r,int x,int y,int o){
//printf("add:k=%d l=%d r=%d x=%d y=%d o=%d\n",k,l,r,x,y,o);
if(x<=l&&r<=y){
//printf(" o=%d k=%d\n",o,k);
addline(o,k);return;
}
if(x<=mid) add(tr[k].ls,l,mid,x,y,o);
if(y>mid) add(tr[k].rs,mid+1,r,x,y,o);
return;
}
区间对区间连边
不需要对入树和出树两两相连,只需要新开一个虚点,都对虚点连边即可
这样总的加边数就是单log的
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=5e5+100;
const int M=1e6+100;
ll read(){
ll x=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){x=x*10+(c^48);c=getchar();}
return x*f;
}
inline void Max(int &x,int y){if(x<y) x=y;}
int n;
struct node{
int to,nxt,w;
}p[N*205];
int fi[N<<3],cnt;
inline void addline(int x,int y,int w){
//if(w)
//printf("%d %d %d\n",x,y,w);
p[++cnt]=(node){y,fi[x],w};fi[x]=cnt;
return;
}
int ls[N<<3],rs[N<<3],rin,rout,tot;
#define mid ((l+r)>>1)
void build(int &k,int l,int r,int op){
k=++tot;
if(l==r){
//printf("(%d %d) k=%d ls=%d rs=%d op=%d\n",l,r,k,0,0,op);
if(op==0) addline(k,l,0);
else addline(l,k,0);
return;
}
build(ls[k],l,mid,op);build(rs[k],mid+1,r,op);
if(op==0){
addline(k,ls[k],0);addline(k,rs[k],0);
}
else{
addline(ls[k],k,0);addline(rs[k],k,0);
}
//printf("(%d %d) k=%d ls=%d rs=%d op=%d\n",l,r,k,ls[k],rs[k],op);
return;
}
void add(int k,int l,int r,int x,int y,int o,int op){
if(x<=l&&r<=y){
if(op==0) addline(o,k,op);
else addline(k,o,op);
return;
}
if(x<=mid) add(ls[k],l,mid,x,y,o,op);
if(y>mid) add(rs[k],mid+1,r,x,y,o,op);
return;
}
int s,m;
int dis[N<<3],vis[N<<3];
deque<int>q;
int main(){
#ifndef ONLINE_JUDGE
freopen("a.in","r",stdin);
freopen("a.out","w",stdout);
#endif
memset(fi,-1,sizeof(fi));cnt=-1;
n=read();m=read();s=read();tot=n;
build(rin,1,n,0);build(rout,1,n,1);
for(int i=1;i<=m;i++){
int a=read(),b=read(),c=read(),d=read();
/*for(int j=a;j<=b;j++){
for(int k=c;k<=d;k++){
if(j==k) continue;
addline(j,k,1);addline(k,j,1);
}
}
*/
int o=++tot;
add(rin,1,n,a,b,o,0);add(rout,1,n,c,d,o,1);
o=++tot;
add(rin,1,n,c,d,o,0);add(rout,1,n,a,b,o,1);
/*
find(rout,1,n,a,b,c,d);
find(rout,1,n,c,d,a,b);
*/
}
memset(dis,0x3f,sizeof(dis));
q.push_back(s);dis[s]=0;
while(!q.empty()){
int now=q.front();q.pop_front();
if(vis[now]) continue;
vis[now]=1;
for(int i=fi[now];~i;i=p[i].nxt){
int to=p[i].to;
if(dis[now]+p[i].w>=dis[to]) continue;
dis[to]=dis[now]+p[i].w;
if(p[i].w) q.push_back(to);
else q.push_front(to);
}
}
for(int i=1;i<=n;i++) printf("%d\n",dis[i]);
}
/*
*/