BNU 28903 :Unique Path 双连通分量 + 查找边的数量
一道很简单的题,在一张无向图上,去掉所有的双连通分量,求剩下的树(除去双连通分量之后剩下的必为树和单独的点)里面的边的数量,我试着用dfs搜,也就是Tarjan的方法去做,但是一开始DFS写挫了,边的访问没有写好导致无法找出分量的位置,来回调整了好久。
这里处理好之后后面就简单了,找出所有的树然后边相加就行了(用并查集实现的)
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <string> #include <algorithm> #include <vector> #include <queue> #include <list> #include <map> using namespace std; #define N 100005 struct edges { int u,v; int next; } edge[N * 2]; int head[N]; int dfn[N]; bool used[N]; int low[N]; int n,m; int shu,pre; int wei[N]; int ans[N]; long long sum; void add(int u,int v) { edge[shu].u = u; edge[shu].v = v; edge[shu].next = head[u]; head[u] = shu; shu++; } void Init() { memset(edge,0,sizeof(edge)); memset(used,false,sizeof(used)); memset(head,0,sizeof(head)); memset(dfn,0,sizeof(dfn)); memset(low,0,sizeof(low)); pre = 1; int u,v; shu = 0; for(int i=0; i<m; i++) { scanf("%d%d",&u,&v); { add(u,v); add(v,u); } } } void dfs(int u, int t) { dfn[u] = low[u] = pre; pre++; for(int i = head[u]; i; i = edge[i].next) { int v = edge[i].v; if(!dfn[v]) { dfs(v,u); low[u] = min(low[u] , low[v]); if(low[v] <= dfn[u]) used[u] = used[v] = true; continue; } if(v != t) low[u] = min(low[u],dfn[v]); } //cout<<u<<' '<<dfn[u]<<' '<<low[u]<<'a'<<endl; } int find(int x) { if(x == wei[x]) { return x; } int t = find(wei[x]); wei[x] = t; return t; } int main() { int t,cas = 0; scanf("%d",&t); while(t--) { cas++; scanf("%d%d",&n,&m); Init(); for(int i=1; i<=n; i++) { if(!used[i]) { dfs(i,0); } } //cout<<'a'<<endl; for(int i=1;i<=n;i++) { wei[i] = i; ans[i] = 1; } for(int i=1; i<=n; i++) { for(int j = head[i];j;j = edge[j].next) { int v = edge[j].v; if(!used[i] && !used[v]) { int x = find(i); int y = find(v); if(x != y) { wei[y] = x; ans[x] += ans[y]; ans[y] = 0; } } } } // for(int i=1;i<=shu;i++) // { // cout<<ans[i]<<' '; // } // cout<<endl; sum = 0; memset(used,false,sizeof(used)); for(int i=1;i<=n;i++) { int x = find(i); if(!used[x]) { used[x] = true; sum += ans[x] * (ans[x] - 1) / 2; //cout<<sum<<' '<<ans[x]<<' '<<x<<endl; } } printf("Case #%d: %lld\n",cas,sum); } return 0; }
HDU4635 : Strongly connected 强连通分量+出入度判断
既然做了双连通,自然想到了去把强连通也写一下(本质上双连通的写法是按强连通来的)
这道题是求在已知的有向图上最多能加多少条边(不能使已有的图整体强连通)如果本身给的就是强连通图,输出-1。
这道题我们需要的就是把所有的强连通分量都求出来(包括点),然后找出其中能添加的边个数。
因为出入度都为1的点在这里我们不能把其当做是一个独立的强连通分量,所以需要判断出入度。
#include <iostream> #include <cstdio> #include <cstring> #include <cmath> #include <string> #include <algorithm> #include <vector> #include <queue> #include <list> #include <map> using namespace std; #define N 100005 struct Edges { int u,v; int next; } edge[N*2]; int head[N]; int dfn[N]; int low[N]; int fan[N]; bool used[N]; bool use[N]; int n,m,shu,front; vector<int> Vector; int num,pre; int ans[N]; bool in[N]; bool out[N]; void add(int u,int v) { edge[shu].u = u; edge[shu].v = v; edge[shu].next = head[u]; head[u] = shu; shu++; } void Init() { memset(edge,0,sizeof(edge)); memset(head,0,sizeof(head)); memset(dfn,0,sizeof(dfn)); memset(low,0,sizeof(low)); memset(used,false,sizeof(used)); memset(use,false,sizeof(use)); memset(fan,0,sizeof(fan)); int u,v; num = pre = 0; shu = 1; for(int i=0; i<m; i++) { scanf("%d%d",&u,&v); add(u,v); } } void dfs(int u) { //cout<<u<<' '<<edge[head[u]].v<<'b'<<endl; front++; pre++; Vector.push_back(u); dfn[u] = low[u] = pre; used[u] = use[u] = true; for(int i = head[u]; i; i = edge[i].next) { int v = edge[i].v; //cout<<v<<'c'<<endl; if(used[v]) { if(use[v]) low[u] = min(low[u],dfn[v]); //必须加入这个来判重和去重,不然分量的计算会出现偏差 continue; } dfs(v); low[u] = min(low[u],low[v]); } if(low[u] == dfn[u]) { //cout<<u<<endl; num++; int mark = Vector.back(); Vector.pop_back(); fan[mark] = num; use[mark] = false; while(mark != u) { mark = Vector.back(); Vector.pop_back(); fan[mark] = num; use[mark] = false; } } } int main() { int t,cas = 0; scanf("%d",&t); while(t--) { cas++; scanf("%d%d",&n,&m); Init(); for(int i=1; i<=n; i++) { //cout<<i<<' '<<used[i]<<endl; if(!used[i]) { front = -1; Vector.clear(); dfs(i); } } //cout<<num<<endl; if(num == 1) { printf("Case %d: -1\n",cas); continue; } memset(ans,0,sizeof(ans)); memset(in,false,sizeof(in)); memset(out,false,sizeof(out)); for(int i=1;i<=n;i++) { ans[fan[i]]++; } for(int i=1;i<shu;i++) { int x = fan[edge[i].u]; int y = fan[edge[i].v]; if(x != y) { in[y] = true; out[x] = true; } } int sum = N; for(int i=1;i <= num;i++) { if(!in[i] || !out[i])sum = min(sum,ans[i]); } //cout<<sum<<endl; sum = n * (n - 1) - sum * (n - sum) - m; printf("Case %d: %d\n",cas,sum); } return 0; }