bzoj2730
tarjan求割点
我发现我还不会求割点
首先我们发现如果整个图是一个点双,那么要放两个出口。答案是2 c(n, 2)
如果不是,说明这个图存在割点能把图分成很多个部分,那么我们就要把割点求出来,每个点双和割点缩成一个点,这样就构成了一棵树。然后每个度数为一的点都要放一个出口,如果度数大于一就不用放,因为怎么割都和另一边的出口相连。
求割点还是用tarjan,如果一个点的dfn<=low[son],说明儿子没有返祖边,那么这个点去掉肯定使儿子的那个连通块独立,所以这个点就是割点,标记一下,然后dfs染其他非割点的点,然后连边算度数算大小。
奥妙重重的是low[u]=min(low[u],low[v])会错,这也告诉我们还是要按照标准来写。
upd:因为可能v本身是个割点,如果翻上去就无法发现v是割点了
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N = 510; int n, m, all, cnt, top, kase; int dfn[N], low[N], vis[N], belong[N], d[N], size[N]; vector<int> G[N], g[N << 1]; void tarjan(int u, int last) { dfn[u] = low[u] = ++all; int child = 0; for(int i = 0; i < G[u].size(); ++i) { int v = G[u][i]; if(!dfn[v]) { tarjan(v, u); low[u] = min(low[u], low[v]); ++child; if((u != 1 && low[v] >= dfn[u]) || (u == 1 && child > 1) && !belong[u]) { vis[u] = 1; belong[u] = ++cnt; size[cnt] = 1; } } else if(v != last) low[u] = min(low[u], dfn[v]); } } void dfs(int u) { vis[u] = 1; belong[u] = cnt; ++size[cnt]; for(int i = 0; i < G[u].size(); ++i) { int v = G[u][i]; if(!vis[v]) dfs(v); } } int main() { // freopen("bzoj_2730.in", "r", stdin); // freopen("bzoj_2730.out", "w", stdout); while(scanf("%d", &n)) { if(!n) break; for(int i = 1; i <= n; ++i) { int u, v; scanf("%d%d", &u, &v); G[u].push_back(v); G[v].push_back(u); m = max(m, max(u, v)); } tarjan(1, 0); for(int i = 1; i <= m; ++i) if(!vis[i]) { ++cnt; dfs(i); } for(int i = 1; i <= m; ++i) for(int j = 0; j < G[i].size(); ++j) { int v = G[i][j]; if(belong[i] != belong[v]) { g[belong[i]].push_back(belong[v]); g[belong[v]].push_back(belong[i]); } } if(cnt == 1) { printf("Case %d: 2 %lld\n", ++kase, (ll)m * (ll)(m - 1ll) / 2ll); memset(size, 0, sizeof(size)); memset(belong, 0, sizeof(belong)); memset(dfn, 0, sizeof(dfn)); memset(low, 0, sizeof(low)); memset(vis, 0, sizeof(vis)); memset(d, 0, sizeof(d)); memset(belong, 0, sizeof(belong)); for(int i = 1; i <= m; ++i) G[i].clear(); for(int i = 1; i <= cnt; ++i) g[i].clear(); cnt = 0; all = 0; m = 0; top = 0; continue; } for(int i = 1; i <= cnt; ++i) { sort(g[i].begin(), g[i].end()); g[i].erase(unique(g[i].begin(), g[i].end()), g[i].end()); for(int j = 0; j < g[i].size(); ++j) { ++d[i]; ++d[g[i][j]]; } } ll ans1 = 0, ans2 = 1; for(int i = 1; i <= cnt; ++i) if(d[i] == 2) { ++ans1; ans2 *= size[i]; } printf("Case %d: %lld %lld\n", ++kase, ans1, ans2); memset(size, 0, sizeof(size)); memset(belong, 0, sizeof(belong)); memset(dfn, 0, sizeof(dfn)); memset(low, 0, sizeof(low)); memset(vis, 0, sizeof(vis)); memset(d, 0, sizeof(d)); memset(belong, 0, sizeof(belong)); for(int i = 1; i <= m; ++i) G[i].clear(); for(int i = 1; i <= cnt; ++i) g[i].clear(); cnt = 0; all = 0; m = 0; top = 0; } // fclose(stdin); fclose(stdout); return 0; }