问题描述

给出一张有向图,可能存在环,对于所有的i,求出从1号点到i点的所有路径上的必经点集合。

 

什么是支配树

两个简单的小性质——

1.如果i是j的必经点,而j又是k的必经点,则i也是k的必经点。

2.如果i和j都是k的必经点,则i和j之间必然存在必经点关系,不可能互相都不是必经点。

 

不难发现所有的必经点关系形成了一个以1点为根的树形关系,每个点的支配点集合就是其到根节点(1点)路径上的点集,称这棵树为支配树。

 

怎么求支配树

假如我们得到的是一个有向无环图,那么只需要$O(N)$的做一遍拓扑排序就可以了,非常简单。

假如我们得到了一张有向有环图,那么我们可以$O(N)$的枚举一个点,把它从图上删去,从根$O(M)$的DFS(或BFS)一次,就可以知道它是哪些点的必经点,复杂度$O(NM)$,简单粗暴,但时间复杂度难以接受。

然后就有了Lengauer-Tarjan算法,复杂度为$O(NlogN)$,有一堆定理证明,想详细的搞明白最好去看Tarjan的英文论文,网上有些中文翻译难免带些小错误。

 

简单的上手题

据某位大佬说,这个算法还没见到过不是裸题的题…… OTZ

不过确实,目前这个算法一般应用在浅层,题面也是非常的裸,简直就是再说“快来拿支配树上我啊!”

 

CodeChef Counting on a directed graph GRAPHCNT

  1 #include <bits/stdc++.h>
  2  
  3 using namespace std;
  4  
  5 typedef long long lnt;
  6  
  7 const int mxn = 100005;
  8  
  9 int n, m;
 10  
 11 int tim;
 12 int dfn[mxn];
 13 int idx[mxn];
 14 int fat[mxn];
 15 int idm[mxn];
 16 int sdm[mxn];
 17 int anc[mxn];
 18 int tag[mxn];
 19 lnt siz[mxn];
 20 lnt son[mxn];
 21  
 22 vector<int> G[mxn];
 23 vector<int> R[mxn];
 24 vector<int> S[mxn];
 25 vector<int> T[mxn];
 26  
 27 void dfsG(int u)
 28 {
 29     idx[dfn[u] = ++tim] = u;
 30     
 31     for (auto v : G[u])if (!dfn[v])
 32         fat[v] = u, dfsG(v);
 33 }
 34  
 35 void dfsT(int u)
 36 {
 37     siz[u] = 1;
 38     
 39     for (auto v : T[u])
 40         dfsT(v), siz[u] += siz[v];
 41 }
 42  
 43 int find(int u)
 44 {
 45     if (u == anc[u])
 46         return u;
 47     
 48     int r = find(anc[u]);
 49     
 50     if (dfn[sdm[tag[anc[u]]]] < dfn[sdm[tag[u]]])
 51         tag[u] = tag[anc[u]];
 52     
 53     return anc[u] = r;
 54 }
 55  
 56 signed main(void) 
 57 {
 58     cin >> n >> m;
 59     
 60     for (int i = 1, u, v; i <= m; ++i)
 61     {
 62         cin >> u >> v;
 63         G[u].push_back(v);
 64         R[v].push_back(u);
 65     }
 66     
 67     for (int i = 1; i <= n; ++i)
 68         sdm[i] = tag[i] = anc[i] = i;
 69     
 70     dfsG(1);
 71     
 72     for (int i = tim; i > 1; --i)
 73     {
 74         int u = idx[i];
 75         
 76         for (auto v : R[u])if (dfn[v])
 77         {
 78             find(v);
 79             if (dfn[sdm[tag[v]]] < dfn[sdm[u]])
 80                 sdm[u] = sdm[tag[v]];
 81         }
 82         
 83         anc[u] = fat[u];
 84         
 85         S[sdm[u]].push_back(u);
 86         
 87         int t = idx[i - 1];
 88         
 89         for (auto v : S[t])
 90         {
 91             find(v);
 92             if (sdm[tag[v]] == t)
 93                 idm[v] = t;
 94             else
 95                 idm[v] = tag[v];
 96         }
 97         
 98         S[t].clear();
 99     }
100     
101     for (int i = 2; i <= tim; ++i)
102     {
103         int u = idx[i];
104         if (idm[u] != sdm[u])
105             idm[u] = idm[idm[u]];
106     }
107     
108     for (int i = 2; i <= tim; ++i)
109         T[idm[i]].push_back(i);
110     
111     dfsT(1);
112     
113     lnt ans = tim * (tim - 1);
114     
115     for (int i = tim, u; i >= 2; --i)
116     {
117         ++son[u = idx[i]];
118         if (idm[u] != 1)
119             son[idm[u]] += son[u];
120         else
121             ans -= son[u] * (son[u] - 1);
122     }
123     
124     ans >>= 1;
125     
126     cout << ans << endl;
127 } 
View Code

相关文章:

  • 2022-01-11
  • 2022-12-23
  • 2022-12-23
  • 2021-04-17
  • 2021-06-05
  • 2021-06-05
  • 2021-07-02
猜你喜欢
  • 2022-02-09
  • 2021-04-11
  • 2021-05-23
  • 2022-12-23
  • 2021-09-23
  • 2021-11-16
  • 2021-07-22
相关资源
相似解决方案