题目链接https://codeforces.com/contest/787/problem/D
time limit per test:2 seconds
memory limit per test:256 megabytes
Rick and his co-workers have made a new radioactive formula and a lot of bad guys are after them. So Rick wants to give his legacy to Morty before bad guys catch them.
There are n planets in their universe numbered from 1 to n. Rick is in planet number s (the earth) and he doesn’t know where Morty is. As we all know, Rick owns a portal gun. With this gun he can open one-way portal from a planet he is in to any other planet (including that planet). But there are limits on this gun because he’s still using its free trial.
By default he can not open any portal by this gun. There are q plans in the website that sells these guns. Every time you purchase a plan you can only use it once but you can purchase it again if you want to use it more.
Plans on the website have three types:
1.With a plan of this type you can open a portal from planet v to planet u.
2.With a plan of this type you can open a portal from planet v to any planet with index in range [l, r].
3.With a plan of this type you can open a portal from any planet with index in range [l, r] to planet v.
Rick doesn’t known where Morty is, but Unity is going to inform him and he wants to be prepared for when he finds and start his journey immediately. So for each planet (including earth itself) he wants to know the minimum amount of money he needs to get from earth to that planet.
Input
The first line of input contains three integers n, q and s (1 ≤ n, q ≤ 10^5, 1 ≤ s ≤ n) — number of planets, number of plans and index of earth respectively.
The next q lines contain the plans. Each line starts with a number t, type of that plan (1 ≤ t ≤ 3). If t = 1 then it is followed by three integers v, u and w where w is the cost of that plan (1 ≤ v, u ≤ n, 1 ≤ w ≤ 10^ 9). Otherwise it is followed by four integers v, l, r and w where w is the cost of that plan (1 ≤ v ≤ n, 1 ≤ l ≤ r ≤ n, 1 ≤ w ≤ 10^9).
Output
In the first and only line of output print n integers separated by spaces. i-th of them should be minimum money to get from earth to i-th planet, or - 1 if it’s impossible to get to that planet.
Examples
Input
3 5 1
2 3 2 3 17
2 3 2 2 16
2 2 2 3 3
3 3 1 1 12
1 3 3 17
Output
0 28 12
Input
4 3 1
3 4 1 3 12
2 2 3 4 10
1 2 4 16
Output
0 -1 -1 12
Note
In the first sample testcase, Rick can purchase 4th plan once and then 2nd plan in order to get to get to planet number 2.
题目大意:给你n个点,然后m次操作,每次操作建一次边,操作1位u->v建边,操作2为u->[l,r],操作3为[l,r]->v。给你一个起点,问你该点到每个点的最短距离。
首先想到的是dij暴力建图,那么就是u对区间[l,r]的每一个点建立一个边权为w的边,想想就知道不可能实现,不管是内存还是时间都无法承受。
既然是区间和点对应,那么我们可以用线段树,这个有点难想到,建立两颗线段树:
来自yz巨佬的图片博客链接:https://blog.csdn.net/weixin_43741224/article/details/96473255
我们设上面的线段树为a,下面的线段树为b,然后建树的时候记录每个节点的编号,大约为n*8个节点,然后记录每棵树的叶子节点的编号分别为pa,pb,那么操作1就可以直接从叶子节点转移到叶子结点了:
if (opt==1) {
scanf ("%d%d%I64d",&u,&v,&w);
add(pb[u],pa[v],w);
}
这里我们以线段树b的叶子节点为实点,那么起始的位置就是pb[st],最后的距离就是dis[pb[i]]。
需要注意的是下面的树正着建的,所以叶子节点的编号不是紧接着a叶子节点的。
对于操作2,那么我们直接pb[pos]到a的[l,r]建一条边就好了。
操作3,对线段树b的[l,r],建一条到pa[pos]的边就好了。
接下来就是一个正常的dij跑一遍就好了,不过由于是前向星建边,对于这种情况很难准确估计有多少条边,所以建议把结构体数组开大一点。
以下是AC代码:
#include <bits/stdc++.h>
using namespace std;
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define ls rt<<1
#define rs rt<<1|1
#define ll long long
const int mac=1e5+10;
const ll inf=1e17+10;
int treea[mac<<2],treeb[mac<<2];
struct Edge
{
int to,next;
ll w;
}eg[mac*20];
struct node
{
ll w;
int id;
bool operator<(const node&a)const{
return w>a.w;
}
};
ll dis[mac<<3];
bool vis[mac<<3];
int num=0,head[mac*20];
void dij(int st)
{
dis[st]=0;
node tmp=node{0,st};
priority_queue<node>q;
q.push(tmp);
while (!q.empty()){
tmp=q.top();
q.pop();
int now=tmp.id;
if (vis[now]) continue;
vis[now]=true;
for (int i=head[now]; i!=-1; i=eg[i].next){
int v=eg[i].to;
if (vis[v]) continue;
if (dis[v]>dis[now]+eg[i].w){
dis[v]=dis[now]+eg[i].w;
q.push(node{dis[v],v});
}
}
}
}
void add(int u,int v,ll w)
{
eg[++num]=Edge{v,head[u],w};
head[u]=num;
}
int sz=0,pa[mac],pb[mac];
//sz为总的节点编号
void builda(int l,int r,int rt)
{
treea[rt]=++sz;
if (l==r){
pa[l]=treea[rt];
return;
}
int mid=(l+r)>>1;
builda(lson);builda(rson);
add(treea[rt],treea[ls],0);//父亲到左右儿子建边
add(treea[rt],treea[rs],0);
}
void buildb(int l,int r,int rt)
{
treeb[rt]=++sz;
if (l==r){
pb[l]=treeb[rt];
add(pa[l],treeb[rt],0);//a的叶子结点到b的叶子结点建边
return;
}
int mid=(l+r)>>1;
buildb(lson);buildb(rson);
add(treeb[ls],treeb[rt],0);//左右儿子到父节点建边
add(treeb[rs],treeb[rt],0);
}
void updatepos(int l,int r,int rt,int pos,int L,int R,ll val)
{
if (l>=L && r<=R){
add(pb[pos],treea[rt],val);
return;
}
int mid=(l+r)>>1;
if (mid>=L) updatepos(lson,pos,L,R,val);
if (mid<R) updatepos(rson,pos,L,R,val);
}
void updatesqc(int l,int r,int rt,int L,int R,int pos,ll val)
{
if (l>=L && r<=R){
add(treeb[rt],pa[pos],val);
return;
}
int mid=(l+r)>>1;
if (mid>=L) updatesqc(lson,L,R,pos,val);
if (mid<R) updatesqc(rson,L,R,pos,val);
}
int main()
{
int n,m,st;
scanf ("%d%d%d",&n,&m,&st);
memset(head,-1,sizeof head);
builda(1,n,1);
buildb(1,n,1);
while (m--){
int opt,l,r,u,v;
ll w;
scanf ("%d",&opt);
if (opt==1) {
scanf ("%d%d%I64d",&u,&v,&w);
add(pb[u],pa[v],w);
}
else if (opt==2){
scanf ("%d%d%d%I64d",&u,&l,&r,&w);
updatepos(1,n,1,u,l,r,w);
}
else {
scanf ("%d%d%d%I64d",&v,&l,&r,&w);
updatesqc(1,n,1,l,r,v,w);
}
}
for (int i=1; i<=n*8; i++) dis[i]=inf;
dij(pb[st]);
for (int i=1; i<=n; i++){
if (dis[pb[i]]==inf) printf ("-1%c",i==n?'\n':' ');
else printf ("%I64d%c",dis[pb[i]],i==n?'\n':' ');
}
return 0;
}