题目大意:n点n边的基环树,求所有简单路径数(1—>2—>3和3—>2—>1是一种)
思路:
举一个计环数的例子
我们可以看作有k个树,根为环上的每一个点
有些之间有1条路径:同树(如1号和2号)
就是都在一个树上:
设有k棵树,第i课树有ct[i]个节点,则有个路径
有些之间有2条路径:(个数:others)
总共:n*(n-1)/2种
则答案为:n*(n-1)-
问题1:咋知道那些在环上
解决方法1:利用树的性质,利用拓扑排序后所有点除根节点会“消失”
而在基环树里恰恰仅有环上的点会留下(因为环上的点都是根节点)
所以我们只要做一遍topo排序,剩余的点就是换上的点
解决方法2:Tarjan or Kosaraju走起!
有环就缩点呗,这个就是代码量大,没什么特别需要说的
queue<int> q;
void topo() {
while (q.size()) q.pop();
for (int i = 1; i <= n; ++i)
if (in[i] == 1) {
q.push(i);
on[i] = 0;
}
while (q.size()) {
int u = q.front();
q.pop();
for (auto v : g[u]) {
if (!on[v]) continue;
if (--in[v] == 1) {
q.push(v);
on[v] = 0;
}
}
}
}
问题2:咋算ct[]
根据前面的方法,我们知道哪些点是环上的点
那我们跑一边dfs,把所有(出自己)非环的点全部算一遍,sum计数一下即可
dfs如下:
void dfs(int u, int fa) {
++sum;
for (auto v : g[u]) {
if (on[v] || fa == v) continue;
dfs(v, u);
}
}
算结果如下(根据前面所分析的答案来):
int ans = n * (n - 1);
for (int i = 1; i <= n; ++i)
if (on[i]) {
sum = 0;
dfs(i, 0);
ans -= sum * (sum - 1) / 2;
}
完整代码此处不展示(以免成为三键编程员)
告辞!