zoukankan      html  css  js  c++  java
  • 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("
    ")
     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 }
  • 相关阅读:
    hdu 1199 Color the Ball 离散线段树
    poj 2623 Sequence Median 堆的灵活运用
    hdu 2251 Dungeon Master bfs
    HDU 1166 敌兵布阵 线段树
    UVALive 4426 Blast the Enemy! 计算几何求重心
    UVALive 4425 Another Brick in the Wall 暴力
    UVALive 4423 String LD 暴力
    UVALive 4872 Underground Cables 最小生成树
    UVALive 4870 Roller Coaster 01背包
    UVALive 4869 Profits DP
  • 原文地址:https://www.cnblogs.com/mrclr/p/9404994.html
Copyright © 2011-2022 走看看