线段树优化建图学习笔记
线段树优化建图是用来解决一类区间连边的图上问题,大概有这些特征,边数非常巨大,然后要求最短路还是其他的一些量,与虚树的思想类似,考虑保留比较关键的边,利用线段树的区间性质优化边数,从而达到优化建图的效果,好了,大概讲完了,看例题。
CF786B Legacy
个点的有向图,有三种连边方式,共 次,点向点连边,点向区间中所有点连边,区间中所有点向某个点连边,求起始点 到每个点的最短路。
。
注意到我们线段树的基本思想,把一个大区间划分为 个小区间进行合并求解。
对这题来说,点向区间连边只需要用与线段树遍历一样的形式,每次向下递归得到划分出来的这 个子区间,向这些子区间分别连边即可,区间向点连边只需反向即可。
需要注意的是,把线段树上的节点看作一个图中的点的话,被当前区间包含的区间(也即当前线段树区间的子树内部),一定是可以走到的,所以还要线段树上和子代进行连边,边权显然是 ,但我们走入区间和走出区间肯定不能用同一棵线段树啊,首先这走入和走出的意义都是不一样的,其次如果只有一棵树,由于向上向下可以是 ,那任意区间直接最短路都是 ,肯定不对啊。
所以用两颗线段树,一棵存入边,一棵存出边即可,具体实现可以多看下代码,挺好写的。
每次增加 条边,所以增加的总边数不超过 ,还要算上线段树上的边数,点数也一样,最少 。
#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]树上的最短路
暂时咕咕咕,有空来补这道好题的题解。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 因为Apifox不支持离线,我果断选择了Apipost!
· 通过 API 将Deepseek响应流式内容输出到前端