线段树优化建图

前置芝士:动态开点线段树。

先放一个板子题:Legacy

题意放一下:

\(n\) 个点,\(q\) 次操作:

  • \(a\)\(b\) 连一条权值 \(c\) 的有向边。

  • \(a\) 向编号在区间 \([l,r]\) 内的每个点连一条边权为 \(c\) 的有向边。

  • 从编号在区间 \([l,r]\) 内的每个点向 \(a\) 连一条边权为 \(c\) 的有向边。

做完所有操作后,求 \(S\) 点到所有其他点的最短路长度,不连通为 -1

数据范围:\(1\le n,q\le 10^5,1\le c\le 10^9\)

说一下一些思路,首先,如果每次都暴力连边,时间复杂度为 \(O(nq)\),显然不能通过,考虑进行优化。

观察到区间连边这个东西,想一想能够优化区间的东西,例如线段树之类的。

我们既然要进行区间连边,就要能够用较少的点表示出修改区间,这也是我们想到线段树的原因。

我们弄两棵线段树,一棵代表入边(称为入树),另一棵代表出边(称为出树)。在初始化中,入树的边由儿子指向父亲,出树中的边由父亲指向儿子。

我们在区间修改操作中,每一次在这个区间被待修改区间完全包含时,把这个点的节点编号向给出的节点连边,具体是谁向谁连边要看是第几个操作。

这里为了省一些空间,我们使用的是动态开点线段树。

最后跑一个简单的 \(dijkstra\) 算法就好啦!

到这里,基本的思路就说完了,下面放一下代码:

#include<bits/stdc++.h>
#define int long long
#define N 100005
#define M 300005
#define K 20
#define pii pair<int,int>
#define x first
#define y second
#define inf 0x3f3f3f3f3f3f3f3f
using namespace std;
int h[M],e[M*K],w[M*K],ne[M*K],idx;
int n,m,s,rt1,rt2;
int lc[M*K],rc[M*K];
int dis[M],S,cnt;
bool st[M];
void add(int a,int b,int c){//链式前向星建边 
	e[idx]=b;w[idx]=c;ne[idx]=h[a];h[a]=idx++;
}
void build1(int &u,int l,int r){
	if(l==r){
		u=l;
		return;
	}
	u=++cnt;//经典的动态开点 
	int mid=l+r>>1;
	build1(lc[u],l,mid);
	build1(rc[u],mid+1,r);//线段树基本递归 
	add(u,lc[u],0);//上文提到的树内连边 
	add(u,rc[u],0);
}
void build2(int &u,int l,int r){//这里同上 
	if(l==r){
		u=l;
		return;
	}
	u=++cnt;
	int mid=l+r>>1;
	build2(lc[u],l,mid);
	build2(rc[u],mid+1,r);
	add(lc[u],u,0);
	add(rc[u],u,0);
}
void modify(int u,int l,int r,int L,int R,int point,int val,int type){
	if(l>=L&&r<=R){//该区间被目标区间完全包含 
		if(type==2)add(point,u,val);
		else add(u,point,val);//根据操作连边 
		return;
	}
	int mid=l+r>>1;
	if(L<=mid)modify(lc[u],l,mid,L,R,point,val,type);//线段树基本递归 
	if(R>mid)modify(rc[u],mid+1,r,L,R,point,val,type);
}
void dij(int S){//dijkstra板子 
	priority_queue<pii,vector<pii>,greater<pii>>q;
	memset(dis,0x3f,sizeof dis);
	memset(st,0,sizeof st);
	dis[S]=0;
	q.push({dis[S],S});
	while(!q.empty()){
		int t=q.top().y;
		q.pop();
		if(st[t])continue;
		st[t]=1;
		for(int i=h[t];~i;i=ne[i]){
			int j=e[i];
			if(dis[j]>dis[t]+w[i]){
				dis[j]=dis[t]+w[i];
				q.push({dis[j],j});
			}
		}
	}
}
signed main(){
	cin>>n>>m>>S;
	cnt=n;
	memset(h,-1,sizeof h);
	build1(rt1,1,n);
	build2(rt2,1,n);
	while(m--){
		int op,point,nxt,val,L,R;
		cin>>op;
		if(op==1){
			cin>>point>>nxt>>val;//单点连边暴力连一下即可 
			add(point,nxt,val);
		}
		else{
			cin>>point>>L>>R>>val;//区间连边 
			if(op==2)modify(rt1,1,n,L,R,point,val,op); 
			else modify(rt2,1,n,L,R,point,val,op);
		}
	}
	dij(S);//跑最短路,输出答案 
	for(int i=1;i<=n;i++){
		if(dis[i]>=inf)dis[i]=-1;
		cout<<dis[i]<<' ';
	}
	return 0;
}
posted @ 2024-07-10 20:12  zxh923  阅读(11)  评论(0编辑  收藏  举报