题意:
题解:
如何处理从v往
[
l
−
r
]
[l-r]
[l−r]每个点进行连边?
如果暴力连边就有
n
2
n^2
n2条边,无法承受。
考虑如何优化区间建图。
线段树,它可以在区间询问/修改的时候,把一个区间分成
O
(
l
o
g
n
)
O(logn)
O(logn)级别个子区间,分别对应线段树上这么多个点。
就像这样建图,相当于是建立了两颗线段树,因为线段树递归到叶子结点后,表示的就是单个结点了,那么我们用叶子结点直接表示点。
其他的点表示区间,线段树上边的权值为0.
比如说v向2-9的点进行连边。
我们只需要连接
v
与
2
、
3
、
15
、
16
、
9
v与2、3、15、16、9
v与2、3、15、16、9这五个结点即可.
为什么?比如说15这个结点,他间接的连接了4和5这两个结点,所以可以直接连向15这个结点。
代码:
/*Keep on going Never give up*/
//#pragma GCC optimize(3,"Ofast","inline")
#include<bits/stdc++.h>
//#include<iostream>
//#include<string>
//#include<cmath>
//#include<vector>
//#include<algorithm>
#define int long long
#define endl '\n'
using namespace std;
const int maxn=1e6+10;
const int mod=1e9+7;
struct node{
int v,u,w,next;
}edge[5000000];
int head[5000000];
int id[2][maxn];
int cnt,tot;
void add(int u,int v,int w,int flag){ //flag=0 fa->son
if(flag) swap(v,u);
edge[cnt].u=u,edge[cnt].v=v,edge[cnt].w=w;
edge[cnt].next=head[u];head[u]=cnt++;
}
void build(int node,int start,int ends,int flag){
if(start==ends){
id[flag][node]=start;
return ;
}
int mid=(start+ends)/2;
id[flag][node]=++tot;
build(node*2,start,mid,flag);
build(node*2+1,mid+1,ends,flag);
add(id[flag][node],id[flag][node*2],0,flag);
add(id[flag][node],id[flag][node*2+1],0,flag);
}
void update(int node,int start,int ends,int l,int r,int xx,int val,int flag){
if(l<=start&&ends<=r){
add(xx,id[flag][node],val,flag);
return ;
}
int mid=(start+ends)/2;
if(l<=mid) update(node*2,start,mid,l,r,xx,val,flag);
if(mid<r) update(node*2+1,mid+1,ends,l,r,xx,val,flag);
}
bool visited[maxn];
int dis[maxn];
struct S{
int id,dis;
bool operator<(const S & a) const
{return dis>a.dis;}
};
void dij(int s){
priority_queue<S> q;
dis[s]=0;
q.push({s,0});
// cout<<"1111"<<endl;
while(!q.empty()){
//cout<<"1111"<<endl;
auto temp=q.top();q.pop();
//cout<<temp.id<<endl;
if(visited[temp.id]) continue;
visited[temp.id]=true;
for(int i=head[temp.id];~i;i=edge[i].next){
int v=edge[i].v;
int w=edge[i].w;
if(dis[v]>dis[temp.id]+w){
dis[v]=dis[temp.id]+w;
q.push({v,dis[v]});
}
}
}
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
memset(head,-1,sizeof head);
for(int i=0;i<maxn;i++){
visited[i]=false;
dis[i]=1e14;
}
int n,q,s;
cin>>n>>q>>s;
tot=n;
build(1,1,n,0);
build(1,1,n,1);
for(int i=0;i<q;i++){
int opt,x,l,r,w;
cin>>opt;
if(opt==1){
cin>>l>>r>>w;
add(l,r,w,0);
}
else if(opt==2){
cin>>x>>l>>r>>w;
update(1,1,n,l,r,x,w,0);
}
else{
cin>>x>>l>>r>>w;
update(1,1,n,l,r,x,w,1);
}
}
dij(s);
//cout<<"1111"<<endl;
for(int i=1;i<=n;i++){
if(dis[i]==1e14) cout<<-1<<endl;
else cout<<dis[i]<<" ";
}
}