线段树优化建图 学习笔记

线段树优化建图是用于给区间建边的一种较快的方式,可以把区间连向单点的时间复杂度变为 \(O(\log n)\)

思路

我们回顾一下线段树的结构,是不是有若干个区间,每个区间是 \([l,r]\)
当我们进行点对区间建边的时候,我们可以把建边的区间拆分到一个类似线段树的结构上,将点一一和线段树区间中的每个点相连,这样只需要连 \(O(\log n)\) 条边。
但是如果我们要把点连向区间,同时还要把区间连向点的话,在一棵树上就不是很能完成。
于是我们建立两棵树,一颗从上往下,父节点和子节点建一条边权为 \(0\) 的边,一颗从下往上,子节点对父节点建一条边权为 \(0\) 的边。同时在两棵树相对应的叶子节点建一条边权为 \(0\) 的边。在连边时,如果是点连向区间,我们就从第二棵树的节点连向第一棵树的对应节点,否则将第一棵树连向第二棵树。
那么我们就写完了。

代码

拿 CF768B 举例。

codeforces:https://codeforces.com/contest/786/submission/314975891

点击查看代码
#include<bits/stdc++.h>

#define int ll
#define pii pair<int,int> 
#define pll pair<long long,long long> 
#define ll long long
#define i128 __int128

#define mem(a,b) memset((a),(b),sizeof(a))
#define m0(a) memset((a),0,sizeof(a))
#define m1(a) memset(a,-1,sizeof(a))
#define lb(x) ((x)&-(x))
#define lc(x) ((x)<<1)
#define rc(x) (((x)<<1)|1)
#define pb(G,x) (G).push_back((x))
#define For(a,b,c) for(int a=(b);a<=(c);a++)
#define Rep(a,b,c) for(int a=(b);a>=(c);a--)
#define in1(a) a=read()
#define in2(a,b) a=read(), b=read()
#define in3(a,b,c) a=read(), b=read(), c=read()
#define in4(a,b,c,d) a=read(), b=read(), c=read(), d=read()
#define fst first 
#define scd second 
#define dbg puts("IAKIOI")

using namespace std;

int read() {
	int x=0,f=1; char c=getchar();
	for(;c<'0'||c>'9';c=getchar()) f=(c=='-'?-1:1); 
	for(;c<='9'&&c>='0';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
	return x*f;
}
void write(int x) { if(x>=10) write(x/10); putchar('0'+x%10); }

const int mod = 998244353;
int qpo(int a,int b) {int res=1; for(;b;b>>=1,a=(a*a)%mod) if(b&1) res=res*a%mod; return res; }
int inv(int a) {return qpo(a,mod-2); }

#define maxn 200050

int n,m,s;
const int del=5e5;
vector<pii > G[maxn<<3];
bool vis[maxn<<3];
int bott[maxn];

void ade(int u,int v,int w) {
	G[u].push_back({v,w});
//	cout<<"edge:{"<<u<<' '<<v<<' '<<w<<"}\n";
}

void build(int idx,int l,int r) {
	if(l==r) {
		bott[l]=idx;
		ade(idx,idx+del,0);
		return ;
	}
	ade(idx,lc(idx),0);
	ade(idx,rc(idx),0);
	ade(lc(idx)+del,idx+del,0);
	ade(rc(idx)+del,idx+del,0);
	int mid=l+r>>1;
	build(lc(idx),l,mid);
	build(rc(idx),mid+1,r);
}
void add(int idx,int l,int r,int L,int R,int v,int w,int opt) {
	if(L<=l&&r<=R) {
		if(opt==0) ade(del+v,idx,w);
		else ade(del+idx,v,w);
		return ;
	}
	int mid=l+r>>1;
	if(L<=mid) add(lc(idx),l,mid,L,R,v,w,opt);
	if(R>mid) add(rc(idx),mid+1,r,L,R,v,w,opt);
}
int dis[maxn<<3];
void work() {
	in3(n,m,s);
	build(1,1,n);
	For(i,1,m) {
		int opt,v,u,l,r,w;
		in2(opt,v);
		if(opt!=1) in2(l,r);
		else in1(u);
		in1(w);
		if(opt==1) ade(bott[v],bott[u],w);
		else add(1,1,n,l,r,bott[v],w,opt%2);
	}
	priority_queue<pii,vector<pii >,greater<pii > >q;
	while(!q.empty()) q.pop();
	q.push({0,bott[s]});
	mem(dis,0x3f); dis[bott[s]]=0;
	while(!q.empty()) {
		auto [qwq,u] =q.top(); q.pop();
		if(vis[u]) continue;
//		cout<<u<<'\n';
		vis[u]=1;
		for(auto [v,d]:G[u]) if(!vis[v]&&dis[u]+d<dis[v]) {
			dis[v]=dis[u]+d;
			q.push({dis[v],v});
		}
	}
	For(i,1,n) cout<<(dis[bott[i]]>=4557430888798830399?-1:dis[bott[i]])<<' ';
}

signed main() {
//	freopen("data.in","r",stdin);
//	freopen("myans.out","w",stdout);
//	ios::sync_with_stdio(false); 
//	cin.tie(0); cout.tie(0);
	double stt=clock();
	int _=1;
//	_=read();
//	cin>>_;
	For(i,1,_) {
		work();
	}
	cerr<<"\nTotal Time is:"<<(clock()-stt)*1.0/1000<<" second(s)."<<'\n';
	return 0;
}
posted @ 2025-04-11 16:34  coding_goat_qwq  阅读(28)  评论(1)    收藏  举报