1. 程式人生 > >bzoj3887[USACO15JAN]草鑑定Grass Cownoisseur

bzoj3887[USACO15JAN]草鑑定Grass Cownoisseur

bzoj3887
luogu3119

題目描述:John有n塊草場,有m條單向道路j連線。Bessie從1號草場出發,最後回到1號操場。Bessie想要經過若干個草場,她想要經過儘可能多的草場。Bessie只可以逆行一次,求最多的草場數。

輸入格式:第一行,兩個整數n和m。
     接下來m行,每行兩個整數,表示兩個相連的點。

輸出格式:一行一個整數,表示最多的草場數。

輸入樣例:
7 10
1 2
3 1
2 5
2 4
3 7
3 5
3 6
6 5
7 2
4 7

輸出樣例:
6

解析:很明顯可以可以用tarjan將所有的環縮在一起。
   用co[i]表示i點所在的強連通分量,size[i]表示第i個強連通分量中點的個數。
   再進行兩次spfa,求出1號草場到該點所經過的最多點數dis1[i]和該點到1號草場所經過的最多點數dis2[i]。
   列舉每個點作為逆行的店,那麼ans = max(ans, dis1[co[i]] + dis2[j] - size[co[1]]),其中j與co[i]相連。
   注意ans的初值為size[co[1]],並且答案只有在dis1[i] > 0並且dis2[i] > 0時才可以統計。

程式碼如下:

#include<cstdio>
#include<algorithm>
using namespace std;

const int maxn = 1e5 + 5;
int n, m, size[maxn], que[maxn << 2], dis1[maxn], dis2[maxn], vis[maxn], ans;
int hed1[maxn << 1], nxt1[maxn << 1], to1[maxn << 1], cnt1;
int hed2[maxn << 1], nxt2[maxn << 1], to2[maxn << 1], cnt2;
int hed3[maxn << 1], nxt3[maxn << 1], to3[maxn << 1], cnt3;
int dfn[maxn], low[maxn], num, st[maxn], top, col, co[maxn];

int read(void) {
    char c; while (c = getchar(), c < '0' || c > '9'); int x = c - '0';
    while (c = getchar(), c >= '0' && c <= '9') x = x * 10 + c - '0'; return x;
}

void add1(int x, int y) {
    nxt1[++ cnt1] = hed1[x]; hed1[x] = cnt1; to1[cnt1] = y;
}

void add2(int x, int y) {
    nxt2[++ cnt2] = hed2[x]; hed2[x] = cnt2; to2[cnt2] = y;
}

void add3(int x, int y) {
    nxt3[++ cnt3] = hed3[x]; hed3[x] = cnt3; to3[cnt3] = y;
}

void tarjan(int u) { //縮點 
    dfn[u] = low[u] = ++ num;
    st[++ top] = u;
      for (int i = hed1[u]; i ; i = nxt1[i]) {
        int v = to1[i];
          if (!dfn[v]) {
              tarjan(v);
              low[u] = min(low[u], low[v]);
            }
          else if (!co[v]) low[u] = min(low[u], dfn[v]);
      }
      if (dfn[u] == low[u]) {
        ++ col; co[u] = col;
          while (st[top] != u) {
              co[st[top]] = col;
              top --;
            }
        top --;
      }
}

void spfa1(void) {
    int h = 0, t = 1; que[t] = co[1];
      for (int i = 1; i <= n; ++ i) dis1[co[i]] = 0, vis[co[i]] = 0;
    dis1[co[1]] = size[co[1]]; vis[co[1]] = 1;
      while (h ^ t) {
        int u = que[++ h]; vis[u] = 0;
          for (int i = hed2[u]; i ; i = nxt2[i]) {
              int v = to2[i];
                if (dis1[v] < dis1[u] + size[v]) {
                      dis1[v] = dis1[u] + size[v];
                      if (!vis[v]) {
                            vis[v] = 1;
                            que[++ t] = v;
                          }
                    } 
            }
      }
}

void spfa2(void) {
    int h = 0, t = 1; que[t] = co[1];
      for (int i = 1; i <= n; ++ i) dis2[co[i]] = 0, vis[co[i]] = 0;
    dis2[co[1]] = size[co[1]]; vis[co[1]] = 1;
      while (h ^ t) {
        int u = que[++ h]; vis[u] = 0;
          for (int i = hed3[u]; i ; i = nxt3[i]) {
              int v = to3[i];
                if (dis2[v] < dis2[u] + size[v]) {
                      dis2[v] = dis2[u] + size[v];
                      if (!vis[v]) {
                            vis[v] = 1;
                            que[++ t] = v;
                          }
                    } 
            }
      }
}

int main() {
    n = read(); m = read();
      for (int i = 1; i <= m; ++ i) {
        int x = read(), y = read();
        add1(x, y); 
      }
      for (int i = 1; i <= n; ++ i) 
        if (!dfn[i]) tarjan(i);
      for (int i = 1; i <= n; ++ i) 
        for (int j = hed1[i]; j ; j = nxt1[j]) {
          int v = to1[j];
            if (co[i] == co[v]) continue;
          add2(co[i], co[v]); add3(co[v], co[i]);
        }
      for (int i = 1; i <= n; ++ i) size[co[i]] ++;
    spfa1();
    spfa2();
    ans = size[co[1]];
      for (int i = 1; i <= n; ++ i) 
        for (int j = hed3[co[i]]; j ; j = nxt3[j]) 
          if (dis1[co[i]] > 0 && dis2[to3[j]] > 0) 
            ans = max(ans, dis1[co[i]] + dis2[to3[j]] - size[co[1]]);
    printf("%d", ans);
    return 0;
}