tarjan【模板】缩点

传送门:https://www.luogu.org/problemnew/show/P3387

首先呢,tarjan找一个图的强连通分量是基于对图的dfs的。这中间开了一个dfn[]代表dfs序,还有个low[]代表该节点在dfs形成的树中能到达的最近的根。然后分情况进行更新(一会儿看我代码吧)。

为了记录一个强联通分量,我们还要在开一个栈来储存当前查找的强连通分量。如果low[x] == dfn[x],那就说明这个点在dfs树种不能往上再爬了,于是就开始弹栈。

 

对于这道题呢,先用tarjan找出强联通分量,然后缩点建立新图。可以发现,建立的新图是一个拓扑图,所以可以拓扑排序后在图上dp,然而我这么写wa了。翻题解的时候看到了一个更简单的方法:记录每一个点的入度,对于每一个入度为0的点用spfa跑一遍最长路,然后在所有最长路中取一个max即可。

 

  1 #include<cstdio>
  2 #include<iostream>
  3 #include<cstring>
  4 #include<cmath>
  5 #include<algorithm>
  6 #include<cctype>
  7 #include<vector>
  8 #include<stack>
  9 #include<queue>
 10 using namespace std;
 11 #define enter printf("\n")
 12 #define space printf(" ")
 13 #define Mem(a) memset(a, 0, sizeof(a))
 14 typedef long long ll;
 15 typedef double db;
 16 const int INF = 0x3f3f3f3f;
 17 const db eps = 1e-8;
 18 const int maxn = 1e5 + 5;
 19 inline ll read()
 20 {
 21     ll ans = 0;
 22     char ch = getchar(), last = ' ';
 23     while(!isdigit(ch)) {last = ch; ch = getchar();}
 24     while(isdigit(ch))
 25     {
 26         ans = ans * 10 + ch - '0'; ch = getchar();    
 27     }
 28     if(last == '-') ans = -ans;
 29     return ans;
 30 }
 31 inline void write(ll x)
 32 {
 33     if(x < 0) x = -x, putchar('-');
 34     if(x >= 10) write(x / 10);
 35     putchar('0' + x % 10);
 36 }
 37 
 38 vector<int> v[maxn];
 39 int n, m, a[maxn];
 40 
 41 int dfn[maxn], low[maxn], cnt = 0;
 42 bool in[maxn];
 43 stack<int> st;
 44 int col[maxn], ccol = 0, val[maxn];
 45 void tarjan(int now)
 46 {
 47     dfn[now] = low[now] = ++cnt;
 48     st.push(now); in[now] = 1;
 49     for(int i = 0; i < (int)v[now].size(); ++i)
 50     {
 51         if(!dfn[v[now][i]])                    //这个联通块还没找完 
 52         {
 53             tarjan(v[now][i]);
 54             low[now] = min(low[now], low[v[now][i]]);
 55         }
 56         else if(in[v[now][i]]) low[now] = min(low[now], dfn[v[now][i]]);    //这个点找到了他所在联通块的祖先节点 
 57     }
 58     if(low[now] == dfn[now])        //开始找出联通块中的所有点 
 59     {
 60         int x; ccol++;            //ccol相当于染色种数,也就是有多少个连通分量 
 61         do
 62         {
 63             x = st.top();
 64             in[x] = 0; st.pop();
 65             col[x] = ccol; val[ccol] += a[x];    //val[]记录新图上的点的点权 
 66         }while(x != now);
 67     }
 68     return;
 69 }
 70 
 71 vector<int> v2[maxn];
 72 bool du[maxn];
 73 void newGraph(int now)
 74 {
 75     for(int i = 0; i < (int)v[now].size(); ++i)
 76     {
 77         int x = col[now], y = col[v[now][i]];
 78         if(x == y) continue;        //别忘了两个点在一个联通块的情况 
 79         v2[x].push_back(y);
 80         du[y] = 1;
 81     }
 82     return;
 83 }
 84 
 85 
 86 int ans = 0;
 87 
 88 int dis[maxn];
 89 bool vis[maxn];
 90 void spfa(int s)
 91 {
 92     Mem(vis);
 93     for(int i = 1; i <= ccol; ++i) dis[i] = -INF;
 94     queue<int> q;
 95     q.push(s); vis[s] = 1;
 96     dis[s] = val[s];
 97     while(!q.empty())
 98     {
 99         int now = q.front(); q.pop(); vis[now] = 0;
100         for(int i = 0; i < (int)v2[now].size(); ++i)
101         {
102             if(dis[now] + val[v2[now][i]] > dis[v2[now][i]])
103             {
104                 dis[v2[now][i]] = dis[now] + val[v2[now][i]];
105                 if(!vis[v2[now][i]]) {q.push(v2[now][i]); vis[v2[now][i]] = 1;}
106             }
107         }
108     }    
109     for(int i = 1; i <= ccol; ++i) ans = max(ans, dis[i]);        //更新答案 
110 }
111 int main()
112 {
113     n = read(); m = read();
114     for(int i = 1; i <= n; ++i) a[i] = read();
115     for(int i = 1; i <= m; ++i)
116     {
117         int x = read(), y = read();
118         v[x].push_back(y);
119     }
120     for(int i = 1; i <= n; ++i) if(!dfn[i]) tarjan(i);        //有的点从一个定点出发可能走不到,就都得判断是否走过 
121     for(int i = 1; i <= n; ++i) newGraph(i);                //建立新图 
122     for(int i = 1; i <= ccol; ++i) if(!du[i]) spfa(i);        //对于每一个入度为0的点,跑最长路 
123     write(ans); enter;
124     return 0;
125 }

 

posted @ 2018-08-02 08:05  mrclr  阅读(1003)  评论(0编辑  收藏  举报