模板:线段树优化建图

57 篇文章 0 订阅
21 篇文章 0 订阅

前言

百川到海,天下归一

解析

线段树优化建图是用于对一个区间的点连边时的优化方法
建一棵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]);
}
/*

*/

thanks for reading!

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值