CF786B Legacy - 线段树优化建图

题目大意

一开始有 \(n\) 个点,有 \(m\) 个操作,每个操作是以下三种之一:

  • 连边 \(i\to j\),边权为 \(w\);
  • 对于所有 \(i\in [l,r]\),连边 \(i\to j\),边权为 \(w\);
  • 对于所有 \(j\in [l,r]\),连边 \(i\to j\),边权为 \(w\)。

所有操作进行完后问你从 \(s\) 到所有点的最短路。

\(1\le n,m\le 10^5\)。

题解

对于操作一,直接连即可。

对于操作二,建一棵类似于线段树之类的东西,其中的每个点都代表一个区间,同时每个点向其父节点连边(因为某个区间向 \(1\dots n\) 中的某个点连边,就相当于其所有子区间都向这个点连边)。对于线段树中的叶子节点,从它对应的原节点向其连边。将操作中的区间 \([l,r]\) 拆成 线段树上的 \(\log n\) 个区间,这些区间都向这个点连边即可。

对于操作三同理,建一棵反向的线段树即可。

建完图跑一边最短路,时间复杂度 \(O(n \log n \log (n \log n))=O(n\log^2 n)\)。

计算一下这张图中点和边的个数:每棵线段树有 \(4n\) 个点,原图中有 \(n\) 个点,因此点数是 \(9n\);每棵线段树约有 \(4n\) 条边,线段树的叶子节点会连 \(n\) 个边,每次操作最多连 \(\log n\) 条边,于是总边数是 \(10n+m\log n\)。

代码
#include <cstdio>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std;
#define For(Ti,Ta,Tb) for(int Ti=(Ta);Ti<=(Tb);++Ti)
#define Dec(Ti,Ta,Tb) for(int Ti=(Ta);Ti>=(Tb);--Ti)
typedef long long ll;
template<typename T> void Read(T &x){
	x=0;int f=1,ch;
	for(ch=getchar();!isdigit(ch);ch=getchar()) f=(ch=='-'?-1:1);
	for(;isdigit(ch);ch=getchar()) x=x*10+(ch-48);
	x*=f;
}
template<typename T,typename... Args> void Read(T &x,Args&... args){
	Read(x);Read(args...);
}
const int N=1e5+5;
const ll Infll=0x3f3f3f3f3f3f3f3fLL;
int n,q,s,tot=0,num[N];
int head[N*10],cntt=0;
struct Edge{int v,nxt;ll w;}e[int(3e6+5)];
void AddEdge(int u,int v,ll w){
	e[++cntt]=Edge{v,head[u],w};head[u]=cntt;
}
struct Segtree{
	#define ls(xx) ((xx)<<1)
	#define rs(xx) ((xx)<<1|1) 
	int dir;
	struct Node{
		int l,r,v;
	}t[N<<2];
	Segtree(int dir_):dir(dir_){}
	void Build(int p,int l,int r){
		t[p].l=l,t[p].r=r,t[p].v=++tot;
		if(l==r){
			if(~dir) AddEdge(t[p].v,num[l],0);
			else AddEdge(num[l],t[p].v,0);
			return;
		}
		int mid=(l+r)>>1;
		Build(ls(p),l,mid);
		if(~dir) AddEdge(t[p].v,t[ls(p)].v,0);
		else AddEdge(t[ls(p)].v,t[p].v,0);
		Build(rs(p),mid+1,r);
		if(~dir) AddEdge(t[p].v,t[rs(p)].v,0);
		else AddEdge(t[rs(p)].v,t[p].v,0);
	}
	void Add(int p,int l,int r,int x,ll w){
		if(l<=t[p].l&&t[p].r<=r){
			if(~dir) AddEdge(num[x],t[p].v,w);
			else AddEdge(t[p].v,num[x],w);
			return;
		}
		int mid=(t[p].l+t[p].r)>>1;
		if(l<=mid) Add(ls(p),l,r,x,w);
		if(r>mid) Add(rs(p),l,r,x,w);
	}
}tr1(1),tr2(-1);
ll dis[N*10];
void Dijkstra(){
	static int hp[N*10];int len=0;
	auto Cmp=[](int i,int j){return dis[i]>dis[j];};
	memset(dis,0x3f,sizeof(ll)*(tot+2));
	hp[++len]=num[s],dis[num[s]]=0;push_heap(hp+1,hp+len+1,Cmp);
	while(len){
		pop_heap(hp+1,hp+len+1,Cmp);
		int u=hp[len--];
		for(int i=head[u];i;i=e[i].nxt){
			if(dis[e[i].v]>dis[u]+e[i].w){
				dis[e[i].v]=dis[u]+e[i].w;
				hp[++len]=e[i].v;
				push_heap(hp+1,hp+len+1,Cmp);
			}
		}
	}
}
int main(){
	Read(n,q,s);
	For(i,1,n) num[i]=++tot;
	tr1.Build(1,1,n),tr2.Build(1,1,n);
	int op,v,l,r,w;
	For(i,1,q){
		Read(op);
		if(op==1){
			Read(l,r,w);AddEdge(num[l],num[r],w);
		}else if(op==2){
			Read(v,l,r,w);
			tr1.Add(1,l,r,v,w);
		}else if(op==3){
			Read(v,l,r,w);
			tr2.Add(1,l,r,v,w);
		}
	}
	Dijkstra();
	For(i,1,n){
		if(dis[num[i]]!=Infll) printf("%lld ",dis[num[i]]);
		else printf("-1 ");
	}
	return 0;
}
上一篇:构建LNMP+WordPress


下一篇:非常详细的图文安装wordpress安装教程