前言
第一道用线段树优化建边做的题,还是比较有记录意义的
当然也有对Rick姥爷的膜拜 orz
话说Rick为啥会给Morty留遗产
题目
讲解
首先操作一有手就行
问题是点连区间和区间连点的操作不好搞
区间?你会想到什么?
线段树!
直接让线段树上的点连边,表示点连区间和区间连点
但是我们发现一个问题,大区间可以由小区间组成,这会引起什么?
大区间可以走向单点,组成它的小区间也可以走向单点
单点可以走向小区间,单点也可以走向大区间
细品这两句话↑
所以我们需要两棵线段树分别维护走出去(从线段树走出去到单点,后者同理)和走进来
一切都变得简单了,注意long long和数组大小
我才不会用vector去卡空间,哼
代码
int head[MAXN * 9],tot;
struct edge
{
int v,w,nxt;
}e[MAXN * 40];
void Add_Edge(int x,int y,int z)
{
e[++tot].v = y;
e[tot].w = z;
e[tot].nxt = head[x];
head[x] = tot;
}
int lc[MAXN * 9],rc[MAXN * 9],cnt;
int Build(int l,int r,int t)
{
if(l == r) return l;
int mid = (l+r) >> 1;
int x = ++cnt;
lc[x] = Build(l,mid,t);
rc[x] = Build(mid+1,r,t);
if(t == 1) Add_Edge(x,lc[x],0),Add_Edge(x,rc[x],0);//点连区间,往下走
else Add_Edge(lc[x],x,0),Add_Edge(rc[x],x,0);//区间连点,往上走
return x;
}
void update(int x,int l,int r,int ql,int qr,int ID,int val,int t)
{
if(ql <= l && r <= qr)
{
if(t == 1) Add_Edge(ID,x,val);
else Add_Edge(x,ID,val);
return;
}
int mid = (l+r) >> 1;
if(ql <= mid) update(lc[x],l,mid,ql,qr,ID,val,t);
if(mid+1 <= qr) update(rc[x],mid+1,r,ql,qr,ID,val,t);
}
LL dis[MAXN * 9];
struct node
{
int u;LL val;
node(){}
node(int u1,LL val1){
u = u1;
val = val1;
}
bool operator < (const node &px)const{
return val > px.val;
}
};
void dijkstra()
{
for(int i = 1;i <= cnt;++ i) dis[i] = INF;
priority_queue<node> q;
q.push(node(S,dis[S] = 0));
while(!q.empty())
{
node t = q.top(); q.pop();
if(t.val > dis[t.u]) continue;
for(int i = head[t.u]; i ;i = e[i].nxt)
if(dis[e[i].v] > dis[t.u] + e[i].w)
q.push(node(e[i].v,dis[e[i].v] = dis[t.u] + e[i].w));
}
}
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
n = Read(); m = Read(); S = Read();
cnt = n;
rt1 = Build(1,n,1); rt2 = Build(1,n,2);
while(m--)
{
int opt = Read(),u = Read(),l = Read(),r = Read();
if(opt == 1) Add_Edge(u,l,r);
else if(opt == 2) update(rt1,1,n,l,r,u,Read(),1);
else update(rt2,1,n,l,r,u,Read(),2);
}
dijkstra();
for(int i = 1;i <= n;++ i) Put(dis[i] == INF ? -1 : dis[i],' ');
return 0;
}