线段树优化建图模板

题目链接1

题目链接2

        入树是指从叶子节点连有向边连向根,出树是指从根连有向边连向叶子节点,他们的每条边权都是0.如果有一个节点newnode连向出树的一个节点,表示这个节点newnode可以连向出树该节点所管的所有节点;如果有一个节点newnonde连向入树的一个节点,表示入树该节点所管的所有节点都可以连向这个节newnode。

        所以,如果是点x连点y,就是x在入树的编号in_num【x】与y在出树的编号out_num【y】连边。如果是点x连向区间y,就是x在入树的编号in_num【x】与区间y在出树划分出来的log个点连边。如果是区间x连向点y,就是区间x在入树划分出来的log个点连向y在出树的编号out_num【y】。如果是区间x连向区间y,就是new一个节点newnode,让区间x在入树划分的log个节点连向newnode,再让newnode连向区间y在出树划分的log个节点。

        注意点:应该让每个点到自己连一条边权为0的边,in_num【x】连out_num【x】边权为0.只能是入树连向出树,不能出树连入树。区间连区间如果是双向边需要建两个newnode,分别连双边,原因:设x区间连y区间,第一次连x,y,newnode1表示x可以连y,如果再让y连x用newnode1,表示y可以连x区间且y可以连y区间,可是实际上y并不能连y区间,只能连x区间,所以第二次连接要用newnode2。

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
//#define int long long
typedef unsigned long long ull;
typedef pair<ll,ll> pii;
const int inf=0x3f3f3f3f;
const int N=1e6+10;
const int mod=1e9+7;
const ll INF=2e9+10;
mt19937_64 rd(23333);
uniform_real_distribution<double> drd(0.000001,0.99999);


int n,q,s,flag;
struct node{
	int to,next,v;
}e[N*8];
int head[N*8],edge_cnt;
void add(int from ,int to,int v){
	e[++edge_cnt]={to,head[from],v};
	head[from]=edge_cnt;
}
int in_root,out_root,tree_cnt;
int ls[N*8],rs[N*8];
int in_num[N*8],out_num[N*8];
void build_in(int l,int r,int &cur){
	cur=++tree_cnt;
	if(l==r){
		in_num[l]=cur;
		return;
	}
	int mid=(l+r)>>1;
	build_in(l,mid,ls[cur]);
	build_in(mid+1,r,rs[cur]);
	add(ls[cur],cur,0);//儿子连上父亲没有花费
	add(rs[cur],cur,0);
}
void build_out(int l,int r,int &cur){
	cur=++tree_cnt;
	if(l==r){
		out_num[l]=cur;
		return;
	}
	int mid=(l+r)>>1;
	build_out(l,mid,ls[cur]);
	build_out(mid+1,r,rs[cur]);
	add(cur,ls[cur],0);//父亲连儿子没有花费
	add(cur,rs[cur],0);
}
void modify_in(int l,int r,int x,int y,int to,int v,int &cur){
	if(x<=l&&y>=r){//多点到一点,in_tree is leaves to root
		add(cur,to,v);
		return;
	}
	int mid=(l+r)>>1;
	if(x<=mid) modify_in(l,mid,x,y,to,v,ls[cur]);
	if(y>mid) modify_in(mid+1,r,x,y,to,v,rs[cur]);
}
void modify_out(int l,int r,int x,int y,int from,int v,int &cur){
	if(x<=l&&y>=r){//一点到多点,out_tree is root to leaves
		add(from,cur,v);
		return;
	}
	int mid=(l+r)>>1;
	if(x<=mid) modify_out(l,mid,x,y,from,v,ls[cur]);
	if(y>mid) modify_out(mid+1,r,x,y,from,v,rs[cur]);
}
int dis[N*8];
bool vis[N*8];
void dijkstra(int s){
	int x=in_num[s];
	for(int i=1;i<=tree_cnt;i++)
		dis[i]=inf;
	priority_queue<pii,vector<pii>,greater<pii> > q;
	dis[x]=0;
	q.push({0,x});
	while(q.size()){
		auto t=q.top();
		q.pop();
		int index=t.second;
		if(vis[index]) continue;
		vis[index]=1;
		for(int i=head[index];i;i=e[i].next){
			int to=e[i].to,v=e[i].v;
			if(!vis[to]&&dis[to]>dis[index]+v){
				dis[to]=dis[index]+v;
				q.push({dis[to],to});
			}
		}
	}
}

void solve(){
	cin>>n>>q>>s;
	build_in(1,n,in_root);
	build_out(1,n,out_root);
	for(int i=1;i<=n;i++){
		add(in_num[i],out_num[i],0);//点到点本身没有花费
		add(out_num[i],in_num[i],0);
	}
	while(q--){
		/*cin>>flag;
		int x,y,l,r,v;
		if(flag==1){
			cin>>x>>y>>v;
			add(in_num[x],out_num[y],v);
		}
		else if(flag==2){
			cin>>x>>l>>r>>v;
			modify_out(1,n,l,r,in_num[x],v,out_root);
		}
		else{
			cin>>x>>l>>r>>v;
			modify_in(1,n,l,r,out_num[x],v,in_root);
		}*/
		int x,y,l,r,v=1;
		cin>>l>>r>>x>>y;
		int newnode=++tree_cnt;
		modify_in(1,n,l,r,newnode,v,in_root);
		modify_out(1,n,x,y,newnode,v,out_root);
		newnode=++tree_cnt;
		modify_in(1,n,x,y,newnode,v,in_root);
		modify_out(1,n,l,r,newnode,v,out_root);
	}
	dijkstra(s);
	for(int i=1;i<=n;i++){
		if(dis[out_num[i]]==inf) cout<<"-1 ";
		else cout<<dis[out_num[i]]/2<<endl;
	}
}

signed main(){
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    int t=1;
    //cin>>t;
    while(t--){
        solve();
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值