BZOJ1179 APIO2009 Atm 连通性+最短路
题意:给定一张无向图,求1到给定点中,点权和最大的路径
题解:
据说是APIO最水的一道题……Tarjan缩点然后SPFA跑最长路(一开始敲成最短路了QAQ),当然也可以拓扑DP不过更麻烦一些
Tarjan的思路就是像遍历一棵树那样遍历整张图,记录每个节点的编号dfn[i]及其DFS树中子树所能回溯到的最早的节点low[i],如果dfn[i]==low[i],显然其整棵子树上low[x]==low[i]的点全都能回溯到i然后再重新到达其他low[x]==low[i]的点,所以这些点就构成了一个强连通分量。由于DFS序中某点子树是连续的一段,因此可以用栈来记录。
#include <stack> #include <queue> #include <cstdio> #include <cstring> #include <cstdlib> #include <climits> #include <iostream> #include <algorithm> using namespace std; const int MAXN=3000+2; struct HASH{ int u; HASH *next; HASH(){} HASH(int _u,HASH *_next):u(_u),next(_next){} }*table[MAXN],mem[2*MAXN],*node[MAXN]; int N,M,w[MAXN],f[MAXN],S,P,ans=INT_MIN,cnt,d[MAXN]; int dfn[MAXN],low[MAXN],scc,depth; bool flag[MAXN]; stack<int> s; queue<int> q; void Insert1(int u,int v){ table[u]=&(mem[cnt++]=HASH(v,table[u]));} void Insert2(int u,int v){ node[u]=&(mem[cnt++]=HASH(v,node[u]));} void Tarjan(int x){ low[x]=dfn[x]=++depth,flag[x]=1,s.push(x); for(HASH *p=table[x];p;p=p->next){ if(!dfn[p->u]){ Tarjan(p->u); low[x]=min(low[x],low[p->u]); } else if(flag[p->u]) low[x]=min(low[x],dfn[p->u]); } if(dfn[x]==low[x]){ scc++; int t=0; while(t!=x){ t=s.top(),s.pop(); f[t]=scc,flag[t]=0; } } } void SPFA(int s){ memset(d,-1,sizeof(d)); memset(flag,0,sizeof(flag)); q.push(s),d[s]=w[s],flag[s]=1; int x; while(!q.empty()){ x=q.front(),q.pop(); for(HASH *p=node[x];p;p=p->next) if(d[p->u]<d[x]+w[p->u]){ d[p->u]=d[x]+w[p->u]; if(!flag[p->u]) flag[p->u]=1,q.push(p->u); } flag[x]=0; } } int main(){ cin >> N >> M; for(int i=1,u,v;i<=M;i++){ cin >> u >> v; Insert1(u,v); } for(int i=1;i<=N;i++) if(!dfn[i]) Tarjan(i); for(int i=1,t;i<=N;i++){ cin >> t; w[f[i]]+=t; } for(int i=1;i<=N;i++) for(HASH *p=table[i];p;p=p->next) if(f[i]!=f[p->u]) Insert2(f[i],f[p->u]); scanf("%d %d",&S,&P); SPFA(f[S]); for(int i=1,t;i<=P;i++){ cin >> t; ans=max(ans,d[f[t]]); } cout << ans << endl; return 0; }