暑训日记Day3 T4《路径》题解 (最短路)
T4 路径
【问题描述】
\(Y\)有一个 \(n\) 个点,\(m\) 条边的无向图,规定起点 \(S\) 和终点 \(T\)(保证 \(S/T\) 联通),一个人从 \(S\) 到 \(T\) 的任意一条最短路都算一条关键路径,而关键路径上的点被称作关键点。假设每个单位 时间能够移动 1 的长度,你需要完成一下任务:对于第 \(i\) 个点,如果其不是关键点,请输出 “\(0\)”;如果是关键点,设从 \(S\) 走最短路到点\(i\) 的时刻为 \(T_i\)(\(0\) 时刻从 \(S\) 出发),输出在时间为 \(T_i\) 时,这个人走关键路径可以到达的不同位置的数量。(注:这个人不一定在节点上,可能 在边上,比如一条长度为 \(7\) 的边,他可以在长度为 \(1,2,3,4,5,6\) 的地方,而不仅限于两个端点)。 你轻易地就解决了这道题。
【输入格式】
第一行为用空格隔开的三个正整数 \(n,S,T\)。
第二行为一个正整数 \(m\)。 接下来 \(m\) 行每行三个正整数 \(x,y,z,\)表示 \(x\) 和 \(y\) 之间有一条长度为 \(z\) 的无向边。
【输出格式】
输出共\(n\) 行。
第 \(i\) 行请输出对应第 \(i\) 个点所应输出信息。
【输入样例】
4 1 4
4
1 2 3
1 3 4
2 4 4
3 4 3
【输出样例】
1
2
2
1
【数据范围】
对于前 20%的数据,$n ≤ 10 $
对于另外 20%的数据,保证是一棵树
对于 100%的数据,\(n ≤ 2000,m ≤ 2*10^5,z ≤ 106\)
【题解】
先跑出最短路,对于在关键路上的边的两个端点 \(u,v\) \((dist[u]<dist[v])\),可以直接把\(dist[u]->dist[v]\)区间中的答案都加一。输出第\(i\)个点的答案时,直接输出\(dist[i]\)所对应的答案。
要说图论这题放T4其实还好,虽然当时我没有打出来(真是\(fw\))
不想再自己敲了,下面是标程。我觉得\(hash\)可以用\(pbds\)里的,还有\(spfa\)不如\(dijkstra\)。
#include<bits/stdc++.h>
using namespace std;
const int Mod=131071;
int N,S,T;
int M;
int dist[2010]={0};
int st=1,en=0;
int num=0;
int HASH[2010]={0};
int dui[131100]={0};
int D[2010]={0};
int Sum[2010]={0};
int Sp=0;
struct bian_{
int st;
int to;
int next;
int dis;
}bian[400010]={{0,0,0,0}};
int First[2010]={0};
int vis[2010]={0};
void Add(int p,int q,int r,int k){
bian[k].next=First[p];
bian[k].st=p;
bian[k].to=q;
bian[k].dis=r;
First[p]=k;
return;
}
void SPFA(){
memset(dist,0x3f3f3f3f,sizeof(dist));
dui[++en]=S;
dist[S]=0;
num=1;
for(;num>0;){
int u=dui[st];
st=(st&Mod)+1;
HASH[u]=0;num--;
for(int i=First[u];i!=0;i=bian[i].next){
int v=bian[i].to;
if(dist[v]>dist[u]+bian[i].dis){
dist[v]=dist[u]+bian[i].dis;
if(HASH[v]==0){
HASH[v]=1;
if(dist[v]<dist[dui[st]]){
st=((st+Mod-1)&Mod)+1;
dui[st]=v;
}
else{
en=(en&Mod)+1;
dui[en]=v;
}
num++;
}
}
}
}
return;
}
int Find(int k){
for(int l=1,r=Sp;l<=r;){
int mid=(l+r)>>1;
if(D[mid]==k) return mid;
if(D[mid]<k) l=mid+1;
else r=mid-1;
}
return 0;
}
int main(){
freopen("path.in","r",stdin);
freopen("path.out","w",stdout);
scanf("%d%d%d",&N,&S,&T);
scanf("%d",&M);
for(int i=1;i<=M;i++){
int p,q,r;
scanf("%d%d%d",&p,&q,&r);
Add(p,q,r,(i<<1)-1);
Add(q,p,r,i<<1);
}
SPFA();
for(int i=1;i<=N;i++)
D[++Sp]=dist[i];
sort(D+1,D+N+1);
Sp=1;
for(int i=2;i<=N;i++)
if(D[i]!=D[i-1])
D[++Sp]=D[i];
for(int i=Sp+1;i<=N;i++) D[i]=0;
en=1;
dui[1]=T;
for(int i=1;i<=en;i++){
int u=dui[i];
for(int p=First[u];p!=0;p=bian[p].next){
int v=bian[p].to;
if(dist[u]==dist[v]+bian[p].dis){
int L=Find(dist[v]),R=Find(dist[u]);
vis[v]=vis[u]=1;
Sum[L+1]++,Sum[R]--;
if(HASH[v]==0)
dui[++en]=v,HASH[v]=1;
}
}
}
for(int i=1;i<=Sp;i++)
Sum[i]+=Sum[i-1];
for(int i=1;i<=N;i++)
if(vis[i]==1)
Sum[Find(dist[i])]++;
for(int i=1;i<=N;i++){
int p=Find(dist[i]);
printf("%d\n",Sum[p]*vis[i]);
}
fclose(stdin);
fclose(stdout);
return 0;
}
看完请留下你的痕迹\(thx\)