[USACO08DEC]Trick or Treat on the Farm

嘟嘟嘟ios

 

這道題有一個特別重要的一點,就是節點數爲 n 的圖只有 n 條邊,因而就有一下幾個性質:git

1.每個點的出度都爲1。ide

2.一個k個節點的強連通份量都是有k條邊的環,並且這個環不會通往其餘的點,只可能有別的點通往這個環。spa

因此說,對於一個在環中的點,答案就是這個環的節點數(包括自環),對於一個不在環中的點,答案就是通往環的距離+環的節點數。code

 

所以,首先用tarjan縮點,而後反向創建縮點後的圖。爲何要反向建呢?先考慮對於上文第二種狀況,暴力的作法就是每一次遇到這樣的一個點,就暴力的一步一步找,直到找到環爲止。然而若是整張圖只有一個自環,時間複雜度就會退化到O(n2)。所以要想辦法維護每個環外的點到環的距離。若是咱們反向建邊,就至關於求每個環到環外點的距離,並且由於環外點之間都只有一條邊,所以路徑是惟一的,因此只要bfs一次就能求出通往這個環的點的距離。blog

 

還有幾點要注意:ci

1.就是上文求出的距離的節點編號都是縮點後的編號,和原圖編號不同,所以咱們要創建一個重新圖編號到原圖編號的映射。又由於這個映射是一對多,因此還得開一個vector存。這個在tarjan的時候就能夠順便處理出來。get

2.縮點後,自環和單點是沒有區別的,因此必須先把是自環的點標記一下。代碼中又開了一個vector,把是自環的點都存了下來(不知有沒有更好的方法)。string

  1 #include<cstdio>
  2 #include<iostream>
  3 #include<algorithm>
  4 #include<cmath>
  5 #include<cstring>
  6 #include<cstdlib>
  7 #include<vector>
  8 #include<queue>
  9 #include<stack>
 10 #include<cctype>
 11 using namespace std;
 12 #define enter puts("")
 13 #define space putchar(' ')
 14 #define Mem(a) memset(a, 0, sizeof(a))
 15 typedef long long ll;
 16 typedef double db;
 17 const int INF = 0x3f3f3f3f;
 18 const db eps = 1e-8;
 19 const int maxn = 1e5 + 5;
 20 inline ll read()
 21 {
 22     ll ans = 0;
 23     char ch = getchar(), last = ' ';
 24     while(!isdigit(ch)) last = ch, ch = getchar();
 25     while(isdigit(ch)) ans = (ans << 3) + (ans << 1) + ch - '0', ch = getchar();
 26     if(last == '-') ans = -ans;
 27     return ans;
 28 }
 29 inline void write(ll x)
 30 { 
 31     if(x < 0) putchar('-'), x = -x;
 32     if(x >= 10) write(x / 10);
 33     putchar(x % 10 + '0');
 34 }
 35 
 36 int n;
 37 vector<int> v[maxn];
 38 
 39 int dfn[maxn], low[maxn], cnt = 0;
 40 bool in[maxn];
 41 stack<int> st;
 42 int col[maxn], val[maxn], ccol = 0;
 43 vector<int> pos[maxn];            //映射 
 44 void tarjan(int now)
 45 {
 46     dfn[now] = low[now] = ++cnt;
 47     st.push(now); in[now] = 1;
 48     for(int i = 0; i < (int)v[now].size(); ++i)
 49     {
 50         if(!dfn[v[now][i]])
 51         {
 52             tarjan(v[now][i]);
 53             low[now] = min(low[now], low[v[now][i]]);
 54         }
 55         else if(in[v[now][i]]) low[now] = min(low[now], dfn[v[now][i]]);
 56     }
 57     if(low[now] == dfn[now])
 58     {
 59         int x; ccol++;
 60         do
 61         {
 62             x = st.top();
 63             in[x] = 0; st.pop();
 64             col[x] = ccol;
 65             pos[ccol].push_back(x);
 66             val[ccol]++;
 67         }while(x != now);
 68     }
 69     return;
 70 }
 71 
 72 vector<int> v2[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[y].push_back(x);            //反向建邊 
 80     }
 81 }
 82 
 83 vector<int> ccir;                    //儲存是自環的點 
 84 int ans[maxn], ans2[maxn];            //ans[]是新圖的節點編號的答案,ans2[]是原圖的 
 85 
 86 void bfs(int s)
 87 {
 88     ans[s] = val[s];
 89     queue<int> q;
 90     q.push(s);
 91     while(!q.empty())
 92     {
 93         int now = q.front(); q.pop();
 94         for(int i = 0; i < v2[now].size(); ++i)
 95         {
 96             ans[v2[now][i]] = ans[now] + 1;
 97             q.push(v2[now][i]);
 98         }
 99     }
100 }
101 
102 int main()
103 {
104     n = read();
105     for(int i = 1; i <= n; ++i) 
106     {
107         int x = read();
108         v[i].push_back(x);
109         if(i == x) ccir.push_back(i);        //該點是自環 
110     }
111     for(int i = 1; i <= n; ++i) if(!dfn[i]) tarjan(i);
112     for(int i = 1; i <= n; ++i) newGraph(i);
113     for(int i = 0; i < (int)ccir.size(); ++i) ans[col[ccir[i]]] = -1;        //把是自環的點在新圖上標記一下 
114     for(int i = 1; i <= ccol; ++i) if(val[i] > 1 || ans[i] == -1) bfs(i);    
115     //若是這個點是一個環(包括自環),就維護全部能到達他的點的距離 
116     for(int i = 1; i <= ccol; ++i) 
117         for(int j = 0; j < (int)pos[i].size(); ++j) ans2[pos[i][j]] = ans[i];    //再將答案映射到原圖上 
118     for(int i = 1; i <= n; ++i) write(ans2[i]), enter;
119     return 0;
120 }
View Code
相關文章
相關標籤/搜索