首页 > 其他分享 >【学习笔记】线段树优化建图

【学习笔记】线段树优化建图

时间:2024-07-20 19:06:55浏览次数:18  
标签:rt int ll make 笔记 建图 push 线段 dis

前言

2023.5.31 贺了线段树优化建图板子。当时那段时间还被 \(bobo\) 一顿乱 \(D\) ,让我多写点 \(DP\) ,数学,少些点重复的数据结构。

2024.7.19 没想到 暑假集训CSP提高模拟2 \(T3\) 放了个线段树优化建图板子,加上之前线段树优化建图代码是贺的,今年寒假本想找时间步一下的结果没去补,现在只好现补了。

基础知识

  • 建图连边时,我们有时会遇到一个点向一段区间内所有的点连一条边,一段区间内所有的点向一个点连一条边,一段区间内所有的点向一段区间内所有的点连一条边的情况,暴力建边时间复杂度是不可接受的。
  • 对于每一个区间,均能转化成线段树上至多 \(\log_{2} n\) 个区间,而这些区间又能被一些特定的点代替。

例题

luogu P6348 [PA2011] Journeys

  • 区间连区间。

    • 建立两棵线段树,分别称作入树、出树。入树上父亲节点向儿子节点连一条边权为 \(0\) 的有向边,出树上儿子节点向父亲节点连一条边权为 \(0\) 的有向边;入树与出树对应节点由入树向出树上连一条边权为 \(0\) 的有向边
    • 找出两段区间在线段树上分别对应的节点后暴力进行连边时间复杂度仍不可接受。考虑进行优化。
    • 每次连边时我们新建一个虚拟节点,出树对应区间向虚拟节点连一条边权为 \(0\) 的有向边,虚拟节点向入树连一条边权为实际权值的有向边。
    • 然后就能进行正常的最短路了。
    • 空间复杂度为 \(O(8n+m)\) , \(m\) 视实际边的方向而定是否要带 \(2\) 的常数。
  • 因为本题中边权只有 \(0/1\) ,可以写 \(01BFS\) 。

  • 常数较大,谨慎食用。

    点击查看代码
    int cnt=0;
    struct SMT_Q_BG
    {
    	struct SegmentTree
    	{
    		int l,r;
    	}tree[4500010];
    	vector<pair<int,int> >e[4500010];
    	int dis[4500010],id[4500010];
    	int lson(int x)
    	{
    		return x*2;
    	}
    	int rson(int x)
    	{
    		return x*2+1;
    	}
    	void build(int rt,int l,int r,int n)
    	{
    		tree[rt].l=l;
    		tree[rt].r=r;
    		e[rt+4*n].push_back(make_pair(rt,0));//对应节点连边
    		if(l==r)
    		{
    			id[l]=rt;//记录下每个位置在线段树上的节点
    			return;
    		}
    		e[lson(rt)].push_back(make_pair(rt,0));//出树连边
    		e[rson(rt)].push_back(make_pair(rt,0));
    		e[rt+4*n].push_back(make_pair(lson(rt)+4*n,0));
    		e[rt+4*n].push_back(make_pair(rson(rt)+4*n,0));//入树连边
    		int mid=(l+r)/2;
    		build(lson(rt),l,mid,n);
    		build(rson(rt),mid+1,r,n);
    	}
    	void update(int rt,int x,int y,int pd,int n,int cnt)
    	{
    		if(x<=tree[rt].l&&tree[rt].r<=y)
    		{
    			if(pd==0)
    			{
    				e[rt].push_back(make_pair(8*n+cnt,0));//出树向虚拟节点连边
    			}
    			else
    			{
    				e[8*n+cnt].push_back(make_pair(rt+4*n,1));//虚拟节点向入树连边
    			}
    			return;
    		}
    		int mid=(tree[rt].l+tree[rt].r)/2;
    		if(x<=mid)
    		{
    			update(lson(rt),x,y,pd,n,cnt);
    		}
    		if(y>mid)
    		{
    			update(rson(rt),x,y,pd,n,cnt);
    		}
    	}
    	void bfs(int s)
    	{
    		int x,i;
    		memset(dis,0x3f,sizeof(dis));
    		deque<int>q;
    		dis[s]=0;
    		q.push_back(s);
    		while(q.empty()==0)
    		{
    			x=q.front();
    			q.pop_front();
    			for(i=0;i<e[x].size();i++)
    			{
    				if(dis[e[x][i].first]>dis[x]+e[x][i].second)
    				{
    					dis[e[x][i].first]=dis[x]+e[x][i].second;
    					if(e[x][i].second==1)
    					{
    						q.push_back(e[x][i].first);
    					}
    					else
    					{
    						q.push_front(e[x][i].first);
    					}
    				}
    			}
    		}
    	}
    }T;
    void add(int a,int b,int c,int d,int n)
    {
    	cnt++;
    	T.update(1,a,b,0,n,cnt);
    	T.update(1,c,d,1,n,cnt);
    }
    int main()
    {
    	int n,m,s,i,a,b,c,d;
    	cin>>n>>m>>s;
    	T.build(1,1,n,n);
    	for(i=1;i<=m;i++)
    	{
    		cin>>a>>b>>c>>d;
    		add(a,b,c,d,n);
    		add(c,d,a,b,n);
    	}
    	T.bfs(T.id[s]);
    	for(i=1;i<=n;i++)
    	{
    		cout<<((i==s)?0:T.dis[T.id[i]+4*n])<<endl;//取到入树的距离
    	}
    	return 0;
    }
    

CF786B Legacy

  • 点连点,点连区间,区间连点。

    • 点连点,直接连。
    • 点连区间和区间连点,找到对应节点后暴力连边即可。
  • 常数较大,谨慎食用。

    点击查看代码
    struct SMT_Q_BG
    {
    	struct SegmentTree
    	{
    		ll l,r;
    	}tree[2000010];
    	vector<pair<ll,ll> >e[2000010];
    	ll dis[2000010],id[2000010],vis[2000010];
    	ll lson(ll x)
    	{
    		return x*2;
    	}
    	ll rson(ll x)
    	{
    		return x*2+1;
    	}
    	void build(ll rt,ll l,ll r,ll n)
    	{
    		tree[rt].l=l;
    		tree[rt].r=r;
    		e[rt+n*4].push_back(make_pair(rt,0));
    		if(l==r)
    		{
    			id[l]=rt;
    			return;
    		}
    		e[lson(rt)].push_back(make_pair(rt,0));
    		e[rson(rt)].push_back(make_pair(rt,0));
    		e[rt+n*4].push_back(make_pair(lson(rt)+n*4,0));
    		e[rt+n*4].push_back(make_pair(rson(rt)+n*4,0));
    		ll mid=(l+r)/2;
    		build(lson(rt),l,mid,n);
    		build(rson(rt),mid+1,r,n);
    	}
    	void update(ll rt,ll x,ll y,ll pd,ll n,ll pos,ll w)
    	{
    		if(x<=tree[rt].l&&tree[rt].r<=y)
    		{
    			if(pd==2)
    			{
    				e[pos].push_back(make_pair(rt+n*4,w));//出树向入树连边
    			}
    			else
    			{
    				e[rt].push_back(make_pair(pos+n*4,w));
    			}
    			return;
    		}
    		ll mid=(tree[rt].l+tree[rt].r)/2;
    		if(x<=mid)
    		{
    			update(lson(rt),x,y,pd,n,pos,w);
    		}
    		if(y>mid)
    		{
    			update(rson(rt),x,y,pd,n,pos,w);
    		}
    	}
    	void dijkstra(ll p)
    	{
    		ll x,i;
    		priority_queue<pair<ll,ll> >q;
    		memset(dis,0x3f,sizeof(dis));
    		memset(vis,0,sizeof(vis));
    		dis[p]=0;
    		q.push(make_pair(-dis[p],-p));
    		while(q.empty()==0)
    		{
    			x=-q.top().second;
    			q.pop();
    			if(vis[x]==0)
    			{
    				vis[x]=1;
    				for(i=0;i<e[x].size();i++)
    				{
    					if(dis[e[x][i].first]>dis[x]+e[x][i].second) 
    					{
    						dis[e[x][i].first]=dis[x]+e[x][i].second;
    						q.push(make_pair(-dis[e[x][i].first],-e[x][i].first));
    					}
    				}
    			}
    		}
    	}
    }T;
    int main()
    {
    	ll n,m,p,pd,w,a,b,c,d,i;
    	scanf("%lld%lld%lld",&n,&m,&p);
    	T.build(1,1,n,n);
    	for(i=1;i<=m;i++)
    	{
    		scanf("%lld",&pd);
    		if(pd==1)
    		{
    			scanf("%lld%lld%lld",&a,&c,&w);
    			T.e[T.id[a]].push_back(make_pair(T.id[c]+n*4,w));//直接连
    		}
    		if(pd==2)
    		{
    			scanf("%lld%lld%lld%lld",&a,&c,&d,&w);
    			T.update(1,c,d,pd,n,T.id[a],w);
    		}
    		if(pd==3)
    		{
    			scanf("%lld%lld%lld%lld",&c,&a,&b,&w);
    			T.update(1,a,b,pd,n,T.id[c],w);
    		}
    	}
    	T.dijkstra(T.id[p]);
    	for(i=1;i<=n;i++)
    	{
    		if(i==p)
    		{
    			printf("0 ");
    		}
    		else
    		{
    			printf("%lld ",(T.dis[T.id[i]+n*4]==0x3f3f3f3f3f3f3f3f)?-1:T.dis[T.id[i]+n*4]);
    		}
    	}
    	return 0;
    }
    

标签:rt,int,ll,make,笔记,建图,push,线段,dis
From: https://www.cnblogs.com/The-Shadow-Dragon/p/18312217

相关文章

  • 学习笔记第六天
    1.循环结构概述 定义:在给定条件成立时,反复执行某程序段; 要素:循环变量初始化语句;            循环的执行条件;      有使循环趋于结束的语句;2.while循环 格式:while(表达式)语句;特点:先判断条件,后执行语句3.do-while循环格式:do语句whi......
  • 线段树
    把给定的区间转换成如图所示的一棵二叉树每次把区间一分为2,左边是左儿子,右边是右儿子对于每个节点的信息,都可以由两个儿子的信息得到如何单点查询/修改可以发现,两个儿子处理的区间没有交集,所以每次只要判断是在左儿子还是在右儿子,不断的递归对于区间查询,每一......
  • 单目和RGB-D稠密建图
    文章目录单目稠密建图立体视觉稠密深度估计更新深度图极线搜索块匹配RGB-D稠密建图点云地图从点云重建网格八叉树地图其他类型的地图地图的用处:定位、导航、避障、重建、交互。导航、避障、重建使用的是稠密地图。单目稠密建图缺点:双目、和移动单目相机,都可......
  • 7/20 训练笔记
    闲话调试约一个下午后发现极大值设小了。CardboardBox考虑开两个堆\(q_1\)和\(a_2\),一个存入所有一颗星星的取法,另一个存入所有两颗星星的取法。每次两颗两颗比较,然后如果某一次取了一颗星星,那么(设这颗星星对应关卡编号为\(i\))把\(b_i-a_i\)压入堆中。还有一些别的......
  • DatawhaleAI夏令营 机器学习方向 学习笔记
    电力需求预测挑战赛理解赛题【训练时序预测模型助力电力需求预测赛题任务给定多个房屋对应电力消耗历史N天的相关序列数据等信息,预测房屋对应电力的消耗。赛题数据赛题数据由训练集和测试集组成,为了保证比赛的公平性,将每日日期进行脱敏,用1-N进行标识。即1为数据集最近一天,......
  • 线段树(原理、构造和区间查询,例题:Balanced Lineup)
    概念原理    线段树是分治法和二叉树的结合,二叉树上的节点都是根据分治得到的。节点所表示的,也就是线段,可以是区间和、最值或者是其他的,,每次分治,左右子树各一半,每个节点的值代表了以它为根的子树上所有节点的值。通过线段树,大区间的解可以从小区间的解合并而来。构......
  • 使用Java和Neo4j构建图数据库应用
    使用Java和Neo4j构建图数据库应用大家好,我是微赚淘客系统3.0的小编,是个冬天不穿秋裤,天冷也要风度的程序猿!在现代应用开发中,图数据库在处理复杂的关系和网络数据时表现出色。Neo4j是一个流行的图数据库,它允许我们以图的形式存储和查询数据。本文将介绍如何使用Java和Neo4j构......
  • 谷粒商城实战笔记-36~44-Vue
    文章目录一,36-前端基础-Vue-介绍&HelloWorld1,MVVM思想直接操作DOM的示例使用Vue和MVVM的示例MVVM与DOM操作的主要区别2,Vue简介3,Vue的使用步骤3.1新建项目3.2安装依赖3.3使用Vue二,37-前端基础-Vue-基本语法&插件安装1,v-model1.1,双向绑定1.2,vue的双向绑定1.2.1html......
  • 谷粒商城实战笔记-35-前端基础-ES6-模块化
    文章目录一,什么是模块化二,export1.`export`语法2.批量导出3.默认导出三,import1,import语法2,批量导入一,什么是模块化模块化编程是一种软件设计技术,它将程序分解为独立的、可复用的部分,每个部分负责执行特定的功能。这种设计方法在多种编程语言中都有应用,包括Jav......
  • C语言数组笔记
    该笔记整理自阮一峰老师的《C语言教程》和部分网上资料声明和初始化数组//声明一个数组并初始化intnums[]={1,2,3,4,5};//完整写法:intnums[5]={1,2,3,4,5};//数组长度可以省略,因为长度系统可以判断出来//声明一个长度为5的数组intnums[5];//相当......