题目:BZOJ2200.
题目大意:给出一张
n
n
n个点
r
r
r条正权双向边
p
p
p条有向边图,其中无向边权一定为正,且所有无向边组成的环缩点后图是一张DAG.
1
≤
n
≤
2.5
∗
1
0
4
,
1
≤
r
,
p
≤
5
∗
1
0
4
1\leq n\leq 2.5*10^4,1\leq r,p\leq 5*10^4
1≤n≤2.5∗104,1≤r,p≤5∗104.
我们可以直接写一个SPFA上去,发现TLE了,然后dijkstra又不能处理负权边.
所以是时候拿出准备已久的神奇A*算法了.
我们先将无向边输入,将所有无向连通块用dfs打上标记 c [ x ] c[x] c[x]表示 x x x属于第 c [ x ] c[x] c[x]个块.
之后我们将所有有向边输入,将一张有向无环图DAG,其中一个节点表示一个无向连通块.
之后我们在这张DAG上跑一遍拓扑排序,每次取出队头,将队头这一块中的所有节点压入一个堆中.然后用dijkstra对堆中每一个节点更新最短路,当更新一个节点时,将与这个节点连边的所有点进行距离更新,若更新的点不在当前这个连通块内,还应将那个点所在连通块的入度 − 1 -1 −1.
重复这一过程直到拓扑排序完成即可.
堆优化dijkstra的时间复杂度为 O ( R log R ) O(R\log R) O(RlogR),DAG上拓扑排序的时间复杂度为 O ( T + P ) O(T+P) O(T+P),所以这个算法的时间复杂度为 O ( T + P + R log R ) O(T+P+R\log R) O(T+P+RlogR).
代码如下:
#include<bits/stdc++.h>
using namespace std;
#define Abigail inline void
typedef long long LL;
const int N=25000,M=50000,INF=(1<<30)-1;
struct side{
int y,next,v;
}e[M*4+9];
int linc[N+9],lind[N+9],top,dis[N+9];
bool use[N+9];
struct block{
int y,next;
}bl[N+9];
int linb[N+9],tb,c[N+9],cnt,deg[N+9];
queue<int>q;
struct node{
int x,v;
node(int xx,int vv){
x=xx;v=vv;
}
bool operator > (const node p)const{
return v>p.v;
}
};
priority_queue<node,vector<node>,greater<node> >qmin;
int n,s;
void insc(int x,int y,int v){
e[++top].y=y;e[top].v=v;e[top].next=linc[x];linc[x]=top;
}
void insd(int x,int y,int v){
e[++top].y=y;e[top].v=v;e[top].next=lind[x];lind[x]=top;
}
void insb(int x,int y){
bl[++tb].y=y;bl[tb].next=linb[x];linb[x]=tb;
}
void dfs(int k){
c[k]=cnt;insb(cnt,k);
for (int i=linc[k];i;i=e[i].next)
if (!c[e[i].y]) dfs(e[i].y);
}
void dijkstra(int k){
for (int i=linb[k];i;i=bl[i].next)
qmin.push(node(bl[i].y,dis[bl[i].y]));
while (!qmin.empty()){
int t=qmin.top().x;qmin.pop();
if (use[t]) continue;
use[t]=1;
for (int i=linc[t];i;i=e[i].next)
if (dis[t]+e[i].v<dis[e[i].y]){
dis[e[i].y]=dis[t]+e[i].v;
qmin.push(node(e[i].y,dis[e[i].y]));
}
for (int i=lind[t];i;i=e[i].next){
if (dis[t]+e[i].v<dis[e[i].y]) dis[e[i].y]=dis[t]+e[i].v;
if (c[t]^c[e[i].y]) deg[c[e[i].y]]--;
if(!deg[c[e[i].y]]) q.push(c[e[i].y]);
}
}
}
void topsort(int s){
dis[s]=0;
for (int i=1;i<=cnt;i++)
if (!deg[i]) q.push(i);
while (!q.empty()){
int t=q.front();q.pop();
dijkstra(t);
}
}
Abigail into(){
int m1,m2,x,y,v;
scanf("%d%d%d%d",&n,&m1,&m2,&s);
for (int i=1;i<=m1;i++){
scanf("%d%d%d",&x,&y,&v);
insc(x,y,v);insc(y,x,v);
}
for (int i=1;i<=m2;i++){
scanf("%d%d%d",&x,&y,&v);
insd(x,y,v);
}
}
Abigail work(){
for (int i=1;i<=n;i++)
if (!c[i]) ++cnt,dfs(i);
for (int i=1;i<=n;i++)
for (int j=lind[i];j;j=e[j].next)
if (c[i]^c[e[j].y]) deg[c[e[j].y]]++;
for (int i=1;i<=n;i++) dis[i]=INF;
topsort(s);
}
Abigail outo(){
for (int i=1;i<=n;i++)
dis[i]<INF>>1?printf("%d\n",dis[i]):printf("NO PATH\n");
//最短路还有从INF转移到比INF小一点的情况,判断比INF/2小就能通过此题
}
int main(){
into();
work();
outo();
return 0;
}