据说这是一道Word Final的题,Orz。。。
原题链接:https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=3136
题意:
给你一个联通图,让你选择一些点,使得这个图的任意一个点消失后,其余的点都能到达某个你选择的点。问你最少选择哪些点,并且输出在最优的情况下,有多少方案。
题解:
一眼看过去,做法很简单,就删掉所有的割点后,考察联通块的个数就好。但这道题满满的坑。。要不怎么是总决赛的题。。
首先如果这个图只有一个联通块,那么答案就应该是任选两个点,这是因为,如果其中一个点挂了,还能走另外一个点。
如果一个联通块有大于一个割点,那么这个联通块就不需要,这是因为两个割点不可能同时挂了。
代码:
#include<iostream> #include<cstring> #include<algorithm> #include<vector> #include<set> #define MAX_V 50004 using namespace std; int V=0; vector<int> G[MAX_V]; int N; int dfn[MAX_V],low[MAX_V],ind=0; bool vis[MAX_V]; bool isCut[MAX_V]; int tot=0; long long ways=1; long long tmp=0; long long cnt=0; bool used[MAX_V]; set<int> se; void init(){ se.clear(); V=ind=cnt=tmp=0; ways=1; memset(used,0,sizeof(used)); memset(dfn,0,sizeof(dfn)); memset(low,0,sizeof(low)); memset(vis,0,sizeof(vis)); memset(isCut,0,sizeof(isCut)); for(int i=0;i<=N+2;i++)G[i].clear(); } void Tarjan(int u,int p) { dfn[u] = low[u] = ++ind; vis[u]=1; int child = 0; for (int i = 0; i < G[u].size(); i++) { int v = G[u][i]; if (v == p)continue; if (!vis[v]) { child++; Tarjan(v, u); low[u] = min(low[v], low[u]); if (p == 0 && child > 1)isCut[u] = 1; if (p&&low[v] >= dfn[u]) isCut[u] = 1; tot+=isCut[u]; } else low[u] = min(dfn[v], low[u]); } } void dfs(int u) { if (used[u] || isCut[u])return; tmp++; used[u] = 1; for (int i = 0; i < G[u].size(); i++){ int v=G[u][i]; if(isCut[v]){ se.insert(v); continue; } dfs(v); } } int main() { int cas = 0; cin.sync_with_stdio(false); while (cin >> N) { tot=0; if (N == 0)break; init(); for (int i = 0; i < N; i++) { int u, v; cin >> u >> v; V = max(V, max(u, v)); G[u].push_back(v); G[v].push_back(u); } Tarjan(1, 0); cout << "Case " << ++cas << ": "; if (tot == 0) { cout << 2 << " " << (long long)V * (V - 1) / 2 << endl; continue; } for (int u = 1; u <= V; u++) { if (isCut[u])continue; tmp = 0; if (!used[u]) { se.clear(); dfs(u); if (se.size() == 1 && tmp) { ways *= tmp; cnt++; } } } cout << cnt << " " << ways << endl; } return 0; }