此题需要了解线段树基础知识

题意点此处 CF786B Legacy

还是建边难!复杂度O(n^2)!!!
而这道题涉及到:v->[l,r] 或是 [l,r]->v 等此类区间建边
我们想到了线段树可以轻松将区间问题 降解为log级别
如下图,我们构建两颗线段树
一颗 出树,一颗 入树
image

入树主要负责接受信息,出树主要负责引出信息~

1.为了传递信息,如上图的边建好边权设为0
2.若从v->[l,r]: 则在出树中找到v所在树上点编号[叶子结点处],入树中我们把[l,r]拆解成几个小区间,如下图:

image

3.同理若从[l,r]->v:反之,还是举个例子:

image

4.还没结束,为了保证图(及线段树)的连通性,我们的两棵树的叶子之间连边,边权为0。

image

所以这样就over
注意:
1.连边要连线段树结点编号,
2.方便的话使用偏移量m,(入树 [1,m] ,出树 [m+1,2*m] )

以梦为码:

    #include<stdio.h>
    #include<bits/stdc++.h>
    using namespace std;
    const int N=1e5+5;
    const int M=5e6+5;
    typedef long long ll;
    int nxt[M],to[M],head[M],a[N],tot,m;
    ll len[M],inf;
    struct segmentree {		//第一棵树是入数,第二颗树是出树 
    	int l,r;
    }T[N*9];
    void add_edge(int u,int v,ll w) {
    	tot++; nxt[tot]=head[u]; to[tot]=v; len[tot]=w; head[u]=tot;
    }
    void Build_tree(int i,int l,int r,int ad) {
    	T[i+ad].l=l; T[i+ad].r=r;
    	if(!ad) {
    		m=max(m,i);
    		if(l==r) {a[l]=i;return;} 
    	} 
    	else if(l==r) {
    		add_edge(i,i+ad,0);	//两个图的叶结点相通
    		return;
    	}
    	int mid=(l+r)>>1;
    	Build_tree(i*2,l,mid,ad); Build_tree(i*2+1,mid+1,r,ad);
    	if(!ad) {add_edge(i,i*2,0); add_edge(i,i*2+1,0);}
    	else {add_edge(i*2+ad,i+ad,0); add_edge(i*2+1+ad,i+ad,0);}
    }

    void one_more(int i,int l,int r,int v,int w) {
    	if(l<=T[i].l&&T[i].r<=r) {
    		add_edge(v+m,i,w); 
    		return;	
    	}
    	int mid=(T[i].l+T[i].r)>>1;
    	if(l<=mid) one_more(i*2,l,r,v,w);
    	if(r>mid) one_more(i*2+1,l,r,v,w);
    }

    void more_one(int i,int l,int r,int v,int w) {
    	if(l<=T[i].l&&T[i].r<=r) {
    		add_edge(i+m,v,w); 
    		return;
    	}
    	int mid=(T[i].l+T[i].r)>>1;
    	if(l<=mid) more_one(i*2,l,r,v,w);
    	if(r>mid) more_one(i*2+1,l,r,v,w);
    }

    ll dis[M];
    struct node {
    	int p; ll w;
    	bool operator<(const node &u) const{
    		return w>u.w;
    	}
    };
    bool mark[M];
    priority_queue<node> Q;
    void DJ(int s) {
    	memset(dis,0x3f,sizeof(dis));
    	dis[s]=0;
    	Q.push((node){s,0});
    	while(!Q.empty()) {
    		int u=Q.top().p; Q.pop();
    		if(mark[u]) continue;
    		mark[u]=true;
    		for(int i=head[u];i;i=nxt[i]) {
    			int v=to[i];
    			if(!mark[v]&&dis[v]>dis[u]+len[i]) {
    				dis[v]=dis[u]+len[i];
    				Q.push((node){v,dis[v]});
    			}
    		}
    	}
    }

    int main() {
    	int n,q,s;
    	scanf("%d%d%d",&n,&q,&s);
    	Build_tree(1,1,n,0);
    	Build_tree(1,1,n,m);
    	for(int i=1;i<=q;i++) {
    		int op,u,v,l,r;ll w;
    		scanf("%d",&op);
    		if(op==1) {
    			scanf("%d%d%lld",&u,&v,&w);
    			add_edge(a[u]+m,a[v],w);
    		} 
    		else if(op==2) {
    			scanf("%d%d%d%lld",&v,&l,&r,&w);
    			one_more(1,l,r,a[v],w);
    		} 
    		else {
    			scanf("%d%d%d%lld",&v,&l,&r,&w);
    			more_one(1,l,r,a[v],w);
    		}
    	}
    	DJ(a[s]);
    	inf=dis[0];
    	for(int i=1;i<=n;i++) {
    		if(dis[a[i]]==inf) printf("-1 ");
    		else printf("%lld ",dis[a[i]]);
    	}
    	return 0;
    }

综上,只需要q*log(n)条边,因为任意一个区间 [l,r] 最多划分为2log(n)个小区间
留一道类似思考题:
戳我 P6348 [PA2011]Journeys