题目大意:有一个n个点的图,求每个点如果被删除后,图中剩余连通块的数量
1<=n<=1e4
思路:也就是求每一个割点出现在几个双联通分量(没有割点的图)中,找双连通分量也是用tarjan,dfn[u]表示u是第几个遍历到的点,low[u]表示从u出发在不经过父子边的情况下能到达的最早的祖先的dfn值,而如果一个点是割点,那么在不经过该点的情况下无法到达该点的祖先也就是low[v]>=dfn[u],注意特判起始点的情况,它可能是假割点
- //#include<__msvc_all_public_headers.hpp>
- #include
- using namespace std;
- const int N = 1e4 + 5;
- int head[N], head2[N];
- struct Edge
- {
- int v, next;
- }e[N*10];
- int cnt = 0;
- int n,m;
- int dfn[N];
- void addedge(int u, int v)
- {
- e[++cnt].v = v;
- e[cnt].next = head[u];
- head[u] = cnt;
- }
- struct Node
- {
- int id, cn;
- }node[N];
- int tot = 0;
- void init()
- {
- tot = 0;
- cnt = 0;
- for (int i = 0; i <= n; i++)
- {
- node[i].id = i;
- node[i].cn = 1;//答案至少是1
- head[i] = -1;
- dfn[i] = 0;
- }
- }
- int tarjan(int u,int fa)
- {
- int lowu = dfn[u] = ++tot;
- int child = 0;
- for (int i = head[u]; ~i; i = e[i].next)
- {
- int v = e[i].v;
- if (!dfn[v])
- {
- child++;
- int lowv = tarjan(v, u);
- lowu = min(lowu, lowv);
- if (lowv >= dfn[u])
- {//该点是割点
- node[u].cn++;
- }
- }
- else if (dfn[v] < dfn[u] && v != fa)
- {
- lowu = min(lowu, dfn[v]);
- }
- }
- if (fa < 0 && child == 1)
- {//假割点
- node[u].cn = 1;
- }
- return lowu;
- }
- bool cmp(Node a, Node b)
- {
- return a.cn == b.cn ? a.id
b.cn; - }
- void solve()
- {
- init();
- int u, v;
- while(cin>>u>>v)
- {
- if (u == -1 && v == -1)
- {
- break;
- }
- addedge(u, v);
- addedge(v, u);
- }
- tarjan(0, -1);
- sort(node, node + n, cmp);
- for (int i = 0; i < m; i++)
- {
- cout << node[i].id << " " << node[i].cn << endl;
- }
- cout << endl;
- }
- int main()
- {
- ios::sync_with_stdio(false);
- cin.tie(0);
- int t;
- //cin >> t;
- while (cin>>n>>m)
- {
- if (!n&&!m)
- {
- break;
- }
- solve();
- }
- return 0;
- }