题目链接:https://www.luogu.org/problem/P2921

思路:

这个题目我们可以先把强连通分量求出来,(强连通分量:在该子图中如果v->u,那么u->v一定成立)。

然后如果一个强连通分量的点数大于等于2,说明该强连通分量成环,而等于1的强连通分量,

要么自己成环,要么直接或间接连着一个环。

那么我们先用tarjan求强连通分量,然后处理下缩点后的有向无环图就好解决了。


  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstring>
  4 #include <algorithm>
  5 #include <queue>
  6 #include <stack>
  7 #include <map>
  8 #include <cmath>
  9 #include <iomanip>
 10 using namespace std;
 11
 12 typedef long long LL;
 13 #define inf (1LL << 25)
 14 #define rep(i,j,k) for(int i = (j); i <= (k); i++)
 15 #define rep__(i,j,k) for(int i = (j); i < (k); i++)
 16 #define per(i,j,k) for(int i = (j); i >= (k); i--)
 17 #define per__(i,j,k) for(int i = (j); i > (k); i--)
 18
 19 const int N = 100010;
 20 int DFN[N];
 21 int LOW[N];
 22 bool insta[N];
 23 int sccno[N]; //NO.x 编号
 24 int sccdeep[N]; //分量深度
 25 int head[N];
 26 bool visscc[N]; //缩点后的有向无环图访问情况
 27 stack<int> sta;
 28 vector<int> scc[N];
 29 int scccnt,dfn,cnt;
 30 int n;
 31
 32 struct Edge{
 33     int loc;
 34     int to;
 35     int next;
 36 }e[N];
 37
 38 void add(int u,int v){
 39     e[cnt].loc = u;
 40     e[cnt].to = v;
 41     e[cnt].next = head[u];
 42     head[u] = cnt++;
 43 }
 44
 45 void tarjan(int u){
 46
 47
 48     DFN[u] = LOW[u] = ++dfn;
 49     sta.push(u);
 50     insta[u] = true;
 51
 52     for(int o = head[u]; ~o; o = e[o].next){
 53         int v = e[o].to;
 54
 55         if(!DFN[v]){
 56             tarjan(v);
 57             LOW[u] = min(LOW[u],LOW[v]);
 58         }
 59         else if(insta[v]){
 60             LOW[u] = min(LOW[u],DFN[v]);
 61         }
 62     }
 63
 64     if(LOW[u] == DFN[u]){
 65         ++scccnt;
 66         scc[scccnt].clear();
 67         while(1){
 68             int v = sta.top();
 69             sta.pop();
 70             sccno[v] = scccnt; //缩点
 71             insta[v] = false;
 72             scc[scccnt].push_back(v); //缩点
 73
 74             if(v == u) break;
 75         }
 76
 77         sccdeep[scccnt] = scc[scccnt].size(); //包含几个点
 78         // cout << scccnt << " num " << sccdeep[scccnt] << endl;
 79     }
 80 }
 81
 82 void dfs(int ucnt){
 83
 84     //xcnt  属于哪一块分量
 85
 86     int u = scc[ucnt][0]; //该点
 87     visscc[ucnt] = true;
 88     for(int o = head[u]; ~o; o = e[o].next){
 89         int v = e[o].to; //去的点
 90         int vcnt = sccno[v];
 91         if(v == u) break;//自己成环
 92         if(visscc[vcnt]) sccdeep[ucnt] += sccdeep[vcnt];
 93         else{
 94             dfs(vcnt);
 95             sccdeep[ucnt] += sccdeep[vcnt];
 96         }
 97
 98         /*这里给一组数据方便理解
 99             5
100             2
101             3
          1
102 5 103 1 104 */ 105 } 106 } 107 108 void print(){ 109 110 rep(i,1,n){ 111 cout << sccdeep[sccno[i]] << endl; 112 } 113 } 114 115 int main(){ 116 117 ios::sync_with_stdio(false); 118 cin.tie(0); 119 120 cin >> n; 121 122 rep(i,1,n) head[i] = -1; // 123 cnt = 0; //边数 124 int v; 125 rep(u,1,n){ 126 cin >> v; 127 add(u,v); 128 } 129 130 // rep(i,1,n){ 131 // for(int o = head[i]; ~o; o = e[o].next) 132 // cout << i << ' ' << e[o].to << endl; 133 // } 134 135 rep(i,1,n) insta[i] = sccno[i] = DFN[i] = LOW[i] = sccdeep[i] = 0; 136 scccnt = dfn = 0; 137 rep(i,1,n) if(!DFN[i]){ 138 tarjan(i); 139 } 140 141 142 rep(i,1,scccnt) if(sccdeep[i] >= 2) visscc[i] = true; //点数超过2个的分量是环,先处理了 143 rep(i,1,scccnt) if(!visscc[i]){ 144 dfs(i); 145 } 146 147 print(); 148 149 getchar(); getchar(); 150 return 0; 151 }
08-16 10:08