分析:
线段树优化连边模板题,一开始琢磨了半天能不能只用一颗线段树,好像不太现实......
贴一下学长课件上的题解:
线段树优化建图。
建立两棵线段树,其上点的点权分别表示“到达这个区间内所有点的最小花费”和“到达这个区间内任意一个点的最小花费”。
对于第一种路直接加边即可
对于第二种路,添加从v到第一棵线段树对应区间中的点的边
对于第三种路,添加从第二棵线段树对应区间中的点到v的边
然后直接跑堆优化Dijkstra即可。
代码:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cctype>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
typedef long long LL;
const int MAXN=100005;
inline LL read(){
LL x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return x*f;
}
int n,q,s,ecnt,head[MAXN<<4],pos[MAXN],ver,ql,qr;
LL dis[MAXN<<4],k;
bool vis[MAXN<<4];
struct Edge{
int to,nxt;LL w;
}e[MAXN*26];
struct Pair{
int pos;LL dis;
friend bool operator < (Pair x,Pair y){
return x.dis<y.dis;
}
friend bool operator > (Pair x,Pair y){
return x.dis>y.dis;
}
};
inline Pair Mp(int x,LL y){
Pair ret;ret.pos=x,ret.dis=y;return ret;
}
inline void add_edge(int bg,int ed,LL val){
ecnt++;
e[ecnt].to=ed;
e[ecnt].nxt=head[bg];
e[ecnt].w=val;
head[bg]=ecnt;
}
#define mid ((l+r)>>1)
#define lc (o<<1)
#define rc ((o<<1)|1)
void build(int o,int l,int r){
if(l==r){
pos[l]=o;
return;
}
add_edge(o,lc,0);
add_edge(o,rc,0);
add_edge(mid-l+1==1?lc:lc+(n<<2),o+(n<<2),0);
add_edge(r-mid==1?rc:rc+(n<<2),o+(n<<2),0);
build(lc,l,mid);
build(rc,mid+1,r);
}
void upd(int o,int l,int r,int typ){
if(ql<=l&&r<=qr){
if(typ==2) add_edge(pos[ver],o,k);
else add_edge(l==r?o:o+(n<<2),pos[ver],k);
return;
}
if(mid>=ql) upd(lc,l,mid,typ);
if(mid<qr) upd(rc,mid+1,r,typ);
}
priority_queue<Pair,vector<Pair>,greater<Pair> > pq;
inline void dijkstra(){
memset(dis,0x3f,sizeof dis);
while(!pq.empty()) pq.pop();
pq.push(Mp(pos[s],0));
while(!pq.empty()){
int now=pq.top().pos;LL len=pq.top().dis;pq.pop();
if(vis[now]) continue;
vis[now]=1;
dis[now]=len;
for(register int i=head[now];i;i=e[i].nxt){
int ver=e[i].to;
if(!vis[ver]&&dis[ver]>dis[now]+e[i].w){
dis[ver]=dis[now]+e[i].w;
pq.push(Mp(ver,dis[ver]));
}
}
}
}
int main(){
n=read(),q=read(),s=read();
build(1,1,n);
for(int i=1;i<=q;i++){
int opt=read();
if(opt==1){
int u=read(),v=read();k=read();
add_edge(pos[u],pos[v],k);
}
else if(opt==2){
ver=read(),ql=read(),qr=read(),k=read();
upd(1,1,n,2);
}
else{
ver=read(),ql=read(),qr=read(),k=read();
upd(1,1,n,3);
}
}
dijkstra();
for(int i=1;i<=n;i++){
if(dis[pos[i]]>1e18) printf("-1 ");
else printf("%I64d ",dis[pos[i]]);
}
printf("\n");
return 0;
}