'commit'
This commit is contained in:
102
TangDou/AcWing/DCC/HDU3394.cpp
Normal file
102
TangDou/AcWing/DCC/HDU3394.cpp
Normal file
@@ -0,0 +1,102 @@
|
||||
#include <bits/stdc++.h>
|
||||
using namespace std;
|
||||
const int N = 2e5 + 10, M = 2e6 + 10;
|
||||
|
||||
//链式前向星
|
||||
int e[M], h[N], idx, w[M], ne[M];
|
||||
void add(int a, int b, int c = 0) {
|
||||
e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
|
||||
}
|
||||
|
||||
int dfn[N], low[N], stk[N], top, timestamp;
|
||||
vector<int> bcc[N];
|
||||
int bcnt;
|
||||
unordered_set<int> in;
|
||||
|
||||
int n, m;
|
||||
int ans1, ans2, ans3;
|
||||
|
||||
void deal_circle() {
|
||||
int sum = 0;
|
||||
for (int u : bcc[bcnt])
|
||||
for (int j = h[u]; ~j; j = ne[j])
|
||||
if (in.count(e[j])) sum++; //此边的两个点都在点双中,则此边在点双中
|
||||
|
||||
sum /= 2; //因为a->b,b->a都sum++,重复了,需要除以2,才是边数
|
||||
|
||||
//边数大于点数,嵌套环,每条边都是冲突边
|
||||
if (sum > bcc[bcnt].size()) ans2 += sum;
|
||||
|
||||
//判定割边 (第二种判定割边的办法)
|
||||
//此处写ans3+=sum 或 ans3+=1是一个意思,因为此时只能是两点一线的情况,否则就不是点双。特殊的点双只有一条割边
|
||||
if (sum < bcc[bcnt].size()) ans3 += sum;
|
||||
}
|
||||
|
||||
void tarjan(int u, int fa) {
|
||||
low[u] = dfn[u] = ++timestamp;
|
||||
stk[++top] = u;
|
||||
int son = 0; //子节点个数
|
||||
|
||||
for (int i = h[u]; ~i; i = ne[i]) {
|
||||
int j = e[i];
|
||||
if (j == fa) continue;
|
||||
if (!dfn[j]) {
|
||||
son++; //找到一个子节点
|
||||
tarjan(j, u);
|
||||
low[u] = min(low[u], low[j]);
|
||||
|
||||
//第一种判定割边的办法
|
||||
//参阅一下判割边的模板代码
|
||||
if (low[j] > dfn[u]) ans1++;
|
||||
|
||||
if (low[j] >= dfn[u]) {
|
||||
int x;
|
||||
bcnt++;
|
||||
//针对每个点双而言,都在dfs过程中记录此点双中有哪些点,避免后期二次统计,造成速度下降TLE
|
||||
in.clear();
|
||||
do {
|
||||
x = stk[top--];
|
||||
bcc[bcnt].push_back(x);
|
||||
in.insert(x); //增加点双中有哪些点的记录信息
|
||||
} while (x != j);
|
||||
bcc[bcnt].push_back(u);
|
||||
in.insert(u); //增加点双中有哪些点的记录信息
|
||||
deal_circle(); //在完成点双的记录后,马上开始统计,以免将所有的点双中点的信息完整记录下来,造成MLE,TLE等问题
|
||||
}
|
||||
} else
|
||||
low[u] = min(low[u], dfn[j]);
|
||||
}
|
||||
|
||||
//特判独立点
|
||||
if (fa == -1 && son == 0) bcc[++bcnt].push_back(u);
|
||||
}
|
||||
|
||||
int main() {
|
||||
//文件输入输出
|
||||
#ifndef ONLINE_JUDGE
|
||||
freopen("HDU3394.in", "r", stdin);
|
||||
#endif
|
||||
|
||||
while (~scanf("%d%d", &n, &m) && n + m) {
|
||||
// ans1:割边数量 ans2:冲突边数量,也就是点双中所有边的数量,它们都是存在多个环中 ans3:也是割边的数量,与 ans1相同,但实现的方式不一样
|
||||
ans1 = ans2 = ans3 = idx = top = timestamp = 0;
|
||||
memset(dfn, 0, sizeof dfn);
|
||||
memset(h, -1, sizeof h);
|
||||
// memset(low, 0, sizeof low); //low数组不用重新初始化
|
||||
|
||||
while (m--) {
|
||||
int a, b;
|
||||
scanf("%d %d", &a, &b);
|
||||
a++, b++; //避开下标0,向右推一个,使得下面的 tarjan算法的模板不变
|
||||
if (a != b) add(a, b), add(b, a); //防止重边
|
||||
}
|
||||
// tarjan求点双的模板
|
||||
for (int i = 1; i <= n; i++)
|
||||
if (!dfn[i]) tarjan(i, -1);
|
||||
|
||||
//下面无论是取 ans1还是ans3都是一样的
|
||||
printf("%d %d\n", ans1, ans2);
|
||||
// printf("%d %d\n", ans3, ans2);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user