目录
题目
- 1000ms
- 131072K
蒜头君有一些蒜园子,这些园子是由公路相互连通,其中一些形成回路的参观路线。如果有一条公路被多条参观路线公用,那么就可能会发生冲突;如果一条公路没有在任何一个回路内,那么就不会发生冲突。
简单来说,就是在一个无向图中。如果至少有两个环共用了一些边,那么这些边被认为是“冲突边”。如果一些边不在任何一个环中,这些边被认为是“多余边”。请问有多少条公路有冲突,多少条公路没有冲突,另外这图不一定是连通的。
输入格式
第一行包含两个整数 n(0<n≤10000),m(0≤m≤100000) ,表示蒜园的数量和公路的数量。
接下来有 m 行,每行包含两个整数 u,v(0≤u,v<n) 表示蒜园 u 和蒜园 v 之间有一条公路。
你可以假设这里没有自环没有重边。
输出格式
输出以空格隔开的两个整数,分别表示没有冲突公路数目和有冲突公路的数目。
格式说明
输出时每行末尾的多余空格,不影响答案正确性
样例输入
8 100 11 22 33 03 44 55 66 77 45 7样例输出
1 5
题解:
知识点:tarjan算法、环
分析:
这里我们应该拿出点双,而不是边双来求。
做完点双后,我们来看每一个点双连通分量:
-
当点数 = 边数,形成一个环,什么都不做。
-
当点数 > 边数(一条线段,说明这条边是桥),加到 ans1(第一个问题) 上去。
-
当点数 < 边数,那么就含 1 个以上的环了。这个时候点双内部所有点都加到 ans2(第二个问题) 上去。
代码:
#include<iostream>
#include<algorithm>
#include<cstring>
#include<stack>
#include<set>
#define _for(i,a,b) for (int i=(a);i<(b);i++)
using namespace std;
const int N=1e4+5,M=2e5+5;
struct node{
int u,v;
node(){}
node(int _u,int _v){
u=_u;
v=_v;
}
};
int n,m;
int idx,h[N],e[M],ne[M];
int times=0;
int dfn[N],low[N];
int bcc_cnt=0;
stack<node> S;
int ans1,ans2;
bool vis[N];
void c_plus(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
}
void init(){
idx=0;
memset(h,-1,sizeof(h));
}
void add(int a,int b){
e[idx]=b;
ne[idx]=h[a];
h[a]=idx++;
}
void tarjan(int u,int fa){
vis[u]=true;
dfn[u]=low[u]=++times;
//S.push(node(fa,u));
for (int i=h[u];~i;i=ne[i]){
int v=e[i];
if (dfn[v]==0){
S.push(node(u,v));
tarjan(v,u);
low[u]=min(low[u],low[v]);
if (low[v]>dfn[u]){
ans1++;
}
if (low[v]>=dfn[u]){
++bcc_cnt;
set<int> point;
int sum=0;
while (true){
node x=S.top();
point.insert(x.u);
point.insert(x.v);
S.pop();
if (x.u==u && x.v==v){
break;
}
}
for (auto x: point){//求出该点双的边的数量
for (int i=h[x];~i;i=ne[i]){
int j=e[i];
if (j!=x && point.count(j)){
sum++;
}
}
}
sum>>=1;
if (sum>point.size()){
ans2+=sum;
}
}
}else if (dfn[v]<dfn[u] && v!=fa){
S.push(node(u,v));
low[u]=min(low[u],dfn[v]);
}
}
}
int main(){
c_plus();
init();
cin>>n>>m;
_for(i,0,m){
int a,b;
cin>>a>>b;
add(a,b);
add(b,a);
}
_for(i,0,n){
if (!vis[i]){
tarjan(i,-1);
}
}
cout<<ans1<<' '<<ans2<<endl;
return 0;
}