N0244. 小明去旅游【NOIP2023模拟赛T1】
题目描述
$3202$年$8$月$21$日,小明去西安旅游,发现地铁站已经有$n$个了。
地铁站编号成数字$1,2,...,n$。在特定的地铁站,你可以购买特定的地铁票,地铁票一共有$m$种。
每一张票可以由四个参数$i,l,r,w$表示:你可以从第$i$个地铁站,花费$w$元买一张票,然后从$i$出发坐地铁,在地铁站$[l,r]$之间的任意一站下车。
小明想知道,从地铁站$1$出发,最少要花多少钱才能到达其他地铁站,请帮小明算一下吧。
输入格式
第一行两个正整数$n,m$。
接下来$m$行,每行四个参数$i,l,r,w$。
输出格式
输出$n$个整数,表示答案,如果没法到达,输出$−1$。
样例输入1
3 2
1 2 2 10
2 1 3 1
样例输出1
0 10 11
样例输入2
3 3
1 2 2 10
2 1 3 1
1 1 3 15
样例输出2
0 10 11
数据范围
对于30%的数据:$1\leq n\leq 1000,1\leq m \leq 2000$。
对于70%的数据:$1\leq n \leq 10^5,1\leq m \leq 2\times 10^5$。
对于100%的数据:$1\leq n \leq 5\times 10^5,1\leq m \leq 10^6。1\leq i\leq n,1\leq l\leq r\leq n,1\leq w\leq 10^9$。
题解
考虑怎么样做单源最短路:
如果直接建图连边,还没跑最短路,空间复杂度$O(mn)$。
那么边是连不了的。
考虑朴素的dij为啥不行。
因为边太多队列炸了(。。。。
其实一把边不至于拆开往队列里加,从某个点伸出去的一大把边的dis都一样,优先级一样,直接存一个{dis,l,r}
就好了。
上个图:
就变回了:
那问题在哪?
问题在于访问了太多vis[i]==true
的点,虽然continue;
了答案不会错。
那怎么做到不访问vis[i]==true
的点呢?
考虑校门外的树
这道题:初始一个满区间,每次将一个区间的全部树砍掉,问还剩几棵树?
你发现了,这也是存在一棵树砍太多遍的问题。
如果你用线段树$O(nlogn)$当我没说,但这道题可以几乎线性做法。
那就是并查集。对于每个点,以它右边(包括自己)第一个没被访问过的点为祖先。
这样再遍历每个区间时就可以像遍历链表一样轻松了
代码:for(int i=l;i<=r;i=find(i))
说到链表,这不就是链表删数操作?
用数组下标模拟链表,就可以通过下标删数了。
这样子,因为vis[]
只会被以某种方式遍历一遍,所以复杂度约是线性。
#include<cstdio>
#include<cstring>
#include<queue>
#include<bits/basic_string.h>
#define int long long
using namespace std;
inline int read(){
int s=0,f=1;char c=getchar();
while(c<48 || c>57){if(c=='-')f=-f;c=getchar();}
while(c>=48 && c<=57)s=s*10+c-48,c=getchar();
return s*f;
}
int n,m;
const int MAXM=1e6+10;
const int MAXN=5e5+10;
struct node{
int w,l,r;
node(){}
node(int a,int b,int c){w=a,l=b,r=c;}
bool operator<(const node &x)const{
return w>x.w;
}
};
basic_string <node> edge[MAXM];
priority_queue<node> q;
int dis[MAXN];
int lst[MAXN],nxt[MAXN];
void init(){//初始化链表
for(int i=1;i<=n;i++){
lst[i]=i-1;
nxt[i]=i+1;
}
return ;
}
bool vis[MAXN];
void del(int x){
nxt[lst[x]]=nxt[x];
lst[nxt[x]]=lst[x];
}
void bfs(){//最短路
q.push(node(0,1,1));
dis[1]=0;
while(!q.empty()){
int w=q.top().w,l=q.top().l,r=q.top().r;
q.pop();
for(int i=l;i<=r;i=nxt[i]){
if(vis[i])continue;//第一个还是得访问
dis[i]=w;//这个点的最短路确定了
for(auto it:edge[i]){
q.push(node(w+it.w,it.l,it.r));
}
vis[i]=1;//。。。
del(i);//删点
}
}
return;
}
signed main(){
int id,l,r,w;
memset(dis,-1,sizeof(dis));
n=read(),m=read();
init();
for(int i=0;i<m;i++){
id=read();l=read();r=read();w=read();
edge[id]+=node(w,l,r);
}
bfs();
for(int i=1;i<=n;i++){
printf("%lld ",dis[i]);
}
return 0;
}