[SDOI2010]大陆争霸
Link
Solution
有些城市必须在某些城市被破坏之后才能进入,例如 \(v\) 必须在 \(u\) 之前到,那么 \(v\) 的最短路就必须先被更新,这实际上是一个拓扑关系。我们必须要同时满足拓扑图的更新顺序和 Dijstra 的贪心策略。这个问题的解决办法就是在跑最短路的时候同时维护拓扑图的更新,当拓扑图中一个节点 \(u\) 的所有前驱都被更新后,才能将 \(u\) 加入到最短路更新队列,这样就保证了正确性。
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<queue>
using namespace std;
#define N 3007
#define M 700007
inline int read(){
int x=0,flag=1; char c=getchar();
while(c<'0'||c>'9'){if(c=='-') flag=0;c=getchar();}
while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+c-48;c=getchar();}
return flag? x:-x;
}
struct Node{
int pos,dis;
Node(int pos_=0,int dis_=0):pos(pos_),dis(dis_){}
bool operator <(const Node &X)const{return X.dis<dis;}
};
struct E{
int next,to,dis;
}e[M<<1];
int head[N],cnt=0,tot[N],to[N][N];
priority_queue<Node> Q;
inline void add(int id,int to,int dis){
e[++cnt]=(E){head[id],to,dis};
head[id]=cnt;
}
bool vis[N];
int n,m,in[N],ans[N],dis[N],lim[N];
int main(){
n=read(),m=read();
for(int i=1;i<=m;i++){
int u=read(),v=read(),d=read();
add(u,v,d);
}
for(int i=1;i<=n;i++){
in[i]=read();
for(int j=1;j<=in[i];j++){
int v=read();
to[v][++tot[v]]=i;
}
}
memset(dis,63,sizeof(dis));
dis[1]=ans[1]=0,Q.push(Node(1,0));
while(!Q.empty()){
Node t=Q.top(); Q.pop();
int u=t.pos;
if(vis[u]) continue;
else vis[u]=1;
for(int i=head[u];i;i=e[i].next){
int v=e[i].to;
if(dis[v]>ans[u]+e[i].dis){
dis[v]=ans[u]+e[i].dis;
if(!in[v]){
ans[v]=max(dis[v],lim[v]);
Q.push(Node(v,ans[v]));
}
}
}
for(int i=1;i<=tot[u];i++){
int v=to[u][i];
lim[v]=max(lim[v],ans[u]);
if(!(--in[v])){
ans[v]=max(dis[v],lim[v]);
Q.push(Node(v,ans[v]));
}
}
}
printf("%d",ans[n]);
}
/*
6 6
1 2 1
1 4 3
2 3 1
2 5 2
4 6 2
5 3 2
0
0
0
1 3
0
2 3 5
*/