线段树优化建图学习笔记

线段树优化建图是用来解决一类区间连边的图上问题,大概有这些特征,边数非常巨大,然后要求最短路还是其他的一些量,与虚树的思想类似,考虑保留比较关键的边,利用线段树的区间性质优化边数,从而达到优化建图的效果,好了,大概讲完了,看例题。

CF786B Legacy

n 个点的有向图,有三种连边方式,共 q 次,点向点连边,点向区间中所有点连边,区间中所有点向某个点连边,求起始点 s 到每个点的最短路。

1n,q105

注意到我们线段树的基本思想,把一个大区间划分为 O(logn) 个小区间进行合并求解。

对这题来说,点向区间连边只需要用与线段树遍历一样的形式,每次向下递归得到划分出来的这 log 个子区间,向这些子区间分别连边即可,区间向点连边只需反向即可。

需要注意的是,把线段树上的节点看作一个图中的点的话,被当前区间包含的区间(也即当前线段树区间的子树内部),一定是可以走到的,所以还要线段树上和子代进行连边,边权显然是 0 ,但我们走入区间和走出区间肯定不能用同一棵线段树啊,首先这走入和走出的意义都是不一样的,其次如果只有一棵树,由于向上向下可以是 0 ,那任意区间直接最短路都是 0 ,肯定不对啊。

所以用两颗线段树,一棵存入边,一棵存出边即可,具体实现可以多看下代码,挺好写的。

每次增加 log 条边,所以增加的总边数不超过 q logn,还要算上线段树上的边数,点数也一样,最少 4N+4N=8N

#include<bits/stdc++.h>
#define ll long long
#define N 150005
using namespace std;
ll n,qs,s,A,B,S,T,C,treein[4*N],treeout[4*N],vis[10*N],cnt;
ll dis[10*N];
ll ch; 
vector<pair<ll,ll> >e[N*10];
struct Z
{
	ll num,d;
	bool operator<(Z EX)const
	{
        return d>EX.d;
    }
};
priority_queue<Z> q;

void read(ll& ret)
{
	ret = 0;
	char ch = getchar();
	while (ch < '0' || ch > '9') ch = getchar();
	while (ch >= '0' && ch <= '9') ret = ret * 10 + ch - '0', ch = getchar();
}
void build(ll l,ll r,ll node)
{
	if(l==r)
	{
        treeout[node]=l;
        treein[node]=l;
        return;
    }
    ll mid=(l+r)>>1;
    build(l,mid,node<<1);
    build(mid+1,r,node<<1|1); 
	treeout[node]=++cnt;  
	treein[node]=++cnt;
	e[treeout[node<<1]].push_back(make_pair(treeout[node],0));
    e[treeout[node<<1|1]].push_back(make_pair(treeout[node],0));
    e[treein[node]].push_back(make_pair(treein[node<<1],0));
    e[treein[node]].push_back(make_pair(treein[node<<1|1],0));
   
}
/*
1:点连点
2:点连区间
3:区间连点 
线段树优化建图板子 
对于1操作,直接加边 
对于2操作,从第二棵线段树的叶子节点向第一棵线段树中的对应区间连边
对于3操作,从第二棵线段树中的对应区间向第一棵线段树中的叶子节点连边
*/
void change_in(ll l,ll r,ll node,ll x,ll y,ll now,ll w)
{
	if(r<x||l>y) return;
	if(x<=l&&r<=y)
	{
		e[now].push_back(make_pair(treein[node],w));
		return;
	}
	ll mid=(l+r)>>1;
	change_in(l,mid,node<<1,x,y,now,w);
	change_in(mid+1,r,node<<1|1,x,y,now,w);
}
void change_out(ll l,ll r,ll node,ll x,ll y,ll now,ll w)
{
	if(r<x||l>y) return;
	if(x<=l&&r<=y)
	{
		e[treeout[node]].push_back(make_pair(now,w));
	//	cout<<treeout[node]<<" "<<now<<endl;
		return;
	}
	ll mid=(l+r)>>1;
	change_out(l,mid,node<<1,x,y,now,w);
	change_out(mid+1,r,node<<1|1,x,y,now,w);
}
void Dijkstra()
{
	memset(dis,0x3f,sizeof(dis));
	dis[s]=0;
	q.push({s,0});
	while(!q.empty())
	{
		Z t=q.top();
		q.pop();
		if(vis[t.num]==1) continue;
		vis[t.num]=1;		
		for(vector<pair<ll,ll> > ::iterator i=e[t.num].begin();i!=e[t.num].end();i++)
		{					
			ll now=(*i).first;
			ll cd=(*i).second;
		//	if(now==0) continue;
			if(dis[t.num]+cd<dis[now])
			{
				dis[now]=dis[t.num]+cd;
				q.push({now,dis[now]});
			}
		//	cout<<t.num<<"-->"<<now<<endl;
		}
//		cout<<endl<<endl;
	} 
}
int main()
{
//	memset(dis,127/3,sizeof(dis));
//	cout<<dis[1];
//	return 0;
    read(n);read(qs);read(s);
	cnt=n;
    build(1,n,1);
    
 //   cout<<9999;
    while(qs--)
    {
    	read(ch);
    	if(ch==1)
    	{
    		read(A);read(B);read(C);
    		e[A].push_back(make_pair(B,C));
		}
		if(ch==2)
    	{
    		read(A);read(S);read(T);read(C);
    		change_in(1,n,1,S,T,A,C);
		}
		if(ch==3)
    	{
    		read(A);read(S);read(T);read(C);
    		change_out(1,n,1,S,T,A,C);
		}
	} 
	Dijkstra();
	for(ll i=1;i<=n;i++)
	{
		if(dis[i]==0x3f3f3f3f3f3f3f3f) printf("-1 ");
		else
		{
			printf("%lld ",dis[i]);
		}
	}
	return 0;
} 

[BZOJ4699]树上的最短路

暂时咕咕咕,有空来补这道好题的题解。

posted @   Constant1227  阅读(67)  评论(4编辑  收藏  举报
相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端
点击右上角即可分享
微信分享提示