图的遍历
时空限制1000ms / 128MB
题目描述
给出N个点,M条边的有向图,对于每个点v,求A(v)表示从点v出发,能到达的编号最大的点。
输入输出格式
输入格式:第1 行,2 个整数N,M。
接下来M行,每行2个整数Ui,Vi,表示边(Ui,Vi)。点用1,2,⋯,N编号。
输出格式:N 个整数A(1),A(2),⋯,A(N)。
输入输出样例
说明
• 对于60% 的数据,1≤N,K≤10^3;
• 对于100% 的数据,1≤N,M≤10^5。
一开始以为是道dfs水题,结果忘了有环。。。。。。。最后写了个tarjan缩点加dfs。
然后看了一下他们的题解,发现可以反向建边,然后按编号从大到小dfs。。。。。。。。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<bits/stdc++.h> #define N 100050 using namespace std; struct ss { int to,next; }; ss edg[N]; int head[N]; int now_edge=0; void addedge(int u,int v) { edg[now_edge]=(ss){v,head[u]}; head[u]=now_edge++; } int dfn[N],low[N],color[N],now_clock,now_color; int Stack[N],top; void tarjan(int x) { dfn[x]=low[x]=++now_clock; Stack[top++]=x; for(int i=head[x];i!=-1;i=edg[i].next) { int v=edg[i].to; if(!dfn[v]) { tarjan(v); low[x]=min(low[x],low[v]); } else if(!color[v])low[x]=min(low[x],dfn[v]); } if(low[x]==dfn[x]) { // for(int i=1;i<=4;i++)printf("%d ",color[i]); // printf("\n"); now_color++; while(Stack[top-1]!=x) { color[Stack[--top]]=now_color; } color[Stack[--top]]=now_color; } } int ans[N]; int max_point[N]={0}; int dfs(int x) { // printf("%d\n",x); if(ans[x])return ans[x]; int Max=max_point[x]; for(int i=head[x];i!=-1;i=edg[i].next) Max=max(Max,dfs(edg[i].to)); return ans[x]=Max; } void init() { memset(ans,0,sizeof(ans)); memset(head,-1,sizeof(head)); memset(dfn,0,sizeof(dfn)); memset(low,0,sizeof(low)); memset(color,0,sizeof(color)); now_edge=0; top=1; now_color=0; now_clock=1; } int a[N],b[N]; int main() { int n,m; scanf("%d %d",&n,&m); init(); for(int i=1;i<=m;i++) { scanf("%d %d",&a[i],&b[i]); addedge(a[i],b[i]); } for(int i=1;i<=n;i++)if(!dfn[i])tarjan(i); memset(head,-1,sizeof(head)); now_edge=0; // for(int i=1;i<=n;i++)printf("%d ",color[i]); for(int i=1;i<=m;i++) { if(color[a[i]]!=color[b[i]])addedge(color[a[i]],color[b[i]]); } for(int i=1;i<=n;i++)max_point[color[i]]=max(max_point[color[i]],i); for(int i=1;i<=n;i++)printf("%d%c",dfs(color[i]),i==n?'\n':' '); return 0; }
路漫漫其修远兮,吾将上下而求索