线段树优化建图

线段树优化建图

https://www.cnblogs.com/harryhqg/p/10456862.html

膜你赛T3path

适用情况

当点数很多,并且边集以区间形式 (点-->区间/区间->点) 给出

强行连边复杂度太大,考虑如何维护同一个子区间的连边,使用线段树优化。

建图思想

点向区间连边,可以向下传递;

假如:8 号点要向区间 [2,5] 连边,最后会是蓝边的样子(边权就省略了):

那么区间向点连边呢?

反向再建一棵树。显然叶节点是相同的,所以两棵树会共用叶节点。

倒着的树上传递关系就变为,一个节点连了出边,那么他所代表的区间里所有节点都向外连了一条出边。

这些绿色的边的边权都是0 。

总结:

1.建立两个动态开点线段树,一个是入树,一个是出树

树上节点“权值”即为 p ——区间

入树:儿子向父亲连边权为0的边,出树:父亲向儿子连边权为0的边(很好理解,入树是要去出树的,所以往上走肯定是0,同理,出树向下是0)

2.连边:

首先两棵树的叶子结点连双向边,边权为0

\(u->[l,r]\) 出树上连边

\([l,r]->u\) 入树上连边

Solution

我们对每一条信息都单独开出来一个转移节点,然后就变成了 区间--> 点和 点-->区间的问题了。

但是双向边不能共用一个转移节点。因为这样其实上在 \([l_1,r_1]\)内的每个点之间都连了一条长度为2 的边。

实际做的时候,对每一个单向边都开一个转移节点,然后出边边权都设为 0,入边边权都设为 1就可以了。

还有一个比较妙的方法,是出入边权都设为1 然后最短路的答案除以2 ,也是可以的。

最短路不用 dij ,双端bfs即可 (0扔前面,1扔后面)

my代码:

#include <cstdio>
#include <iostream>
#include <queue>
#include <cstring>
using namespace std;
inline int read() {
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    return x*f; 
}
const int N=12000010;
int n,m,s,rt_in,rt_out,tot;
int ls[N],rs[N];
int in[N],out[N];
int hd[N],e_cnt;
struct node{
    int id,val;
    node(){}
    node(int a,int b):id(a),val(b){}
};
struct edge{
    int to,nxt,w;
    edge(){}
    edge(int a,int b,int c):to(a),nxt(b),w(c){}
}e[N];
inline void add(int x,int y,int z){
    e[++e_cnt]={y,hd[x],z};hd[x]=e_cnt;
}
inline void build_in(int l,int r,int &p){
    p=++tot;
    if(l==r){in[l]=p;return;}
    int mid=(l+r)>>1;
    build_in(l,mid,ls[p]);
    build_in(mid+1,r,rs[p]);
    add(ls[p],p,0);
    add(rs[p],p,0);
}
inline void build_out(int l,int r,int &p){
    p=++tot;
    if(l==r){out[l]=p;return;}
    int mid=(l+r)>>1;
    build_out(l,mid,ls[p]);
    build_out(mid+1,r,rs[p]);
    add(p,ls[p],0);
    add(p,rs[p],0);
}
inline void insert_in(int ql,int qr,int l,int r,int cur,int p){
    if(ql<=l&&r<=qr){add(p,cur,1);return;}
    int mid=(l+r)>>1;
    if(ql<=mid) insert_in(ql,qr,l,mid,cur,ls[p]);
    if(qr>mid) insert_in(ql,qr,mid+1,r,cur,rs[p]);
}
inline void insert_out(int ql,int qr,int l,int r,int cur,int p){
    if(ql<=l&&r<=qr){add(cur,p,1);return;}
    int mid=(l+r)>>1;
    if(ql<=mid) insert_out(ql,qr,l,mid,cur,ls[p]);
    if(qr>mid) insert_out(ql,qr,mid+1,r,cur,rs[p]);
}
int dis[N];
bool vis[N];
void work(){
    deque <node> q;
    memset(dis,0x3f,sizeof(dis));
    dis[in[s]]=0;q.push_front({in[s],0});
    while(!q.empty()){
        int x=q.front().id;q.pop_front();
        if(vis[x])continue;
        vis[x]=1;
        for(int i=hd[x];i;i=e[i].nxt){
            int y=e[i].to,z=e[i].w;
            if(dis[y]>dis[x]+z){
                dis[y]=dis[x]+z;
                if(!vis[y]){
                    if(!z) q.push_front({y,dis[y]});
                    else q.push_back({y,dis[y]});
                }
            }
        }
    }
}
int main(){
    n=read();m=read();s=read();
    build_in(1,n,rt_in);
    build_out(1,n,rt_out);
    for(int i=1;i<=n;i++) add(in[i],out[i],0),add(out[i],in[i],0);
    for(int i=1;i<=m;i++){
        int l1=read(),r1=read(),l2=read(),r2=read();
        tot++;insert_in(l1,r1,1,n,tot,rt_in),insert_out(l2,r2,1,n,tot,rt_out);
        tot++;insert_in(l2,r2,1,n,tot,rt_in),insert_out(l1,r1,1,n,tot,rt_out);
    }
    work();
    for(int i=1;i<=n;i++)
        printf("%d\n",dis[out[i]]/2);
    return 0;
}

hs_black更优秀做法https://www.luogu.com.cn/blog/hs-black/solution-p6348

Legacy

模板——有边权

#include <queue>
#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;
typedef long long ll;
const int N=3000010;
inline int read() {
	int x=0;char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
	return x;
}
int n,m,s,in_rt,out_rt,tree_cnt;
int ls[N],rs[N],in[N],out[N];
int hd[N],to[N],nxt[N],w[N],tot;
inline void add(int x,int y,int z) {
	to[++tot]=y;w[tot]=z;nxt[tot]=hd[x];hd[x]=tot;
}
#define mid ((l+r)>>1)
void build_in(int l,int r,int &p) {
	p=++tree_cnt;
	if(l==r) {
		in[l]=p;return;
	}
	build_in(l,mid,ls[p]);
	build_in(mid+1,r,rs[p]);
	add(ls[p],p,0);add(rs[p],p,0);
}
void build_out(int l,int r,int &p) {
	p=++tree_cnt;
	if(l==r) {
		out[l]=p;return;
	}
	build_out(l,mid,ls[p]);
	build_out(mid+1,r,rs[p]);
	add(p,ls[p],0);add(p,rs[p],0);	
}
void upd_in(int l,int r,int L,int R,int pos,int v,int &p) {
	if(L<=l&&r<=R) {
		add(p,pos,v);return;
	}
	if(L<=mid) upd_in(l,mid,L,R,pos,v,ls[p]);
	if(R>mid) upd_in(mid+1,r,L,R,pos,v,rs[p]);
}
void upd_out(int l,int r,int L,int R,int pos,int v,int &p) {
	if(L<=l&&r<=R) {
		add(pos,p,v);return;
	}
	if(L<=mid) upd_out(l,mid,L,R,pos,v,ls[p]);
	if(R>mid) upd_out(mid+1,r,L,R,pos,v,rs[p]);
}
ll dis[N];
bool vis[N];
struct node{
	int u;ll val;
	node(){}
	node(int u_,ll val_):u(u_),val(val_){}
	bool operator < (const node &x) const {
		return val>x.val;
	}
};
priority_queue<node>Q;
void dij() {
	memset(dis,0x3f,sizeof(dis));
	s=in[s];
	dis[s]=0;
	Q.push(node(s,0));
	while(Q.size()) {
		int x=Q.top().u;Q.pop();
		if(vis[x]) continue;
		vis[x]=1;
		for(int i=hd[x];i;i=nxt[i]) {
			int y=to[i];
			if(dis[y]>dis[x]+w[i]) {
				dis[y]=dis[x]+w[i];
				Q.push(node(y,dis[y]));
			}
		}
	}
}
int main() {
	n=read();m=read();s=read();
	build_in(1,n,in_rt);
	build_out(1,n,out_rt);
	for(int i=1;i<=n;i++) 
		add(in[i],out[i],0),add(out[i],in[i],0);
	int op,l,r,x,v;
	while(m--) {
		op=read();
		if(op==1) {
			l=read();r=read();v=read();
			add(in[l],out[r],v);
		}
		if(op==2) {
			x=read();l=read();r=read();v=read();
			upd_out(1,n,l,r,in[x],v,out_rt);
		}
		if(op==3) {
			x=read();l=read();r=read();v=read();
			upd_in(1,n,l,r,out[x],v,in_rt);			
		}
	}
	dij();
	for(int i=1;i<=n;i++) 
		if(dis[out[i]]==dis[0]) printf("-1 ");
		else printf("%lld ",dis[out[i]]);
	return 0;
}


posted @ 2020-11-10 16:52  ke_xin  阅读(131)  评论(0编辑  收藏  举报