2017暑假集训 div1 连通图(1) POJ3694 &&POJ3177

POJ 3694

题意:一个网络管理员管理一个网络,网络中的电脑直接或间接的相连接,管理员有Q次操作,每次向网络中建立一条新边,向管理员报告桥的个数

思路:先将网络中的桥求出来,在求的过程中进行并查集缩点,在询问的时候,进行最朴素的LCA查找最近公共祖先,在求的过程中判断节点与父节点是不是在同一个集合中,如果不在同一个集合,说明是桥,则这个桥将不存在,将两个集合合并。


#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <algorithm>
using namespace std;
const int maxn = 110000;
vector<int> g[maxn];
int dfn[maxn],low[maxn];
int pre[maxn],fa[maxn];
int num;
int n,m,q;
int d_cnt;
int f(int x)
{
	if(x!=pre[x]) pre[x]=f(pre[x]);
	return pre[x];
}
void hb(int x,int y)
{
	int fx = f(x),fy = f(y);
	if(fx!=fy) pre[fx]=fy;
}
void dfs(int fat,int u)
{
	dfn[u]=low[u]= ++d_cnt;
	fa[u]=fat;
	for(int i=0;i<g[u].size();++i)
	{
	    int v=g[u][i];
	    if(v==fat) continue;
		if(!dfn[v])
		{
			dfs(u,v);
			low[u] = min(low[u],low[v]);
			if(low[v]<=dfn[u])  hb(v,u);
			else    num++;
		}
		else  low[u]=min(low[u],dfn[v]);
	}
}
void tarjan()
{
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    d_cnt=num=0;
    for(int i=1;i<=n;++i)  if(!dfn[i]) dfs(i,i);
}
void judge(int u)
{
	int x=f(u);
	int y=f(fa[u]);
	if(x!=y)
	{
		num--;
		pre[x]=y;
	}
}
void lca(int u,int v)
{
	while(dfn[u]>dfn[v])
	{
		judge(u);u=fa[u];
	}
	while(dfn[v]>dfn[u])
	{
		judge(v);v=fa[v];
	}
	while(u!=v)
	{
		judge(u);  judge(v);
		u = fa[u]; v = fa[v];
	}
}
int main()
{
	int kiss=1;
	while(scanf("%d %d",&n,&m)!=EOF)
	{
	    if(n==0&&m==0) break;
		int u,v;
		for(int i=1;i<=n;++i) g[i].clear();
		for(int i=1;i<=n;++i) pre[i]=i;
		for(int i=0;i<m;i++)
		{
			scanf("%d %d",&u,&v);
            g[u].push_back(v);
            g[v].push_back(u);
		}
		tarjan();
		scanf("%d",&q);
		printf("Case %d:\n",kiss++);
		while(q--)
		{
			scanf("%d %d",&u,&v);
			if(f(u)!=f(v))
			{
				lca(u,v);
			}
			printf("%d\n",num);
		}
		printf("\n");
	}
	return 0;
}





POJ 3177

题目大意:有F个牧场,1<=F<=5000,现在一个牧群经常需要从一个牧场迁移到另一个牧场。奶牛们已经厌烦老是走同一条路,所以有必要再新修几条路,这样它们从一个牧场迁移到另一个牧场时总是可以选择至少两条独立的路。现在F个牧场的任何两个牧场之间已经至少有一条路了,奶牛们需要至少有两条。给定现有的R条直接连接两个牧场的路,F-1<=R<=10000,计算至少需要新修多少条直接连接两个牧场的路,使得任何两个牧场之间至少有两条独立的路。两条独立的路是指没有公共边的路,但可以经过同一个中间顶点

做法:这道题拿道之后知道用连通图,但不知道这个边怎么算。
看了这篇文章后  http://blog.csdn.net/lyy289065406/article/details/6762370 才会的。
就是缩点,把连通分量缩成一棵数,那么需要加的边就是 (叶子节点+1)/2;
叶子节点就是入度为1的边,之后代入上面的式子就可以了。

这种题其实就是 这个模型:给定一个连通的无向图G,至少要添加几条边,才能使其变为双连通图。

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <vector>
#include <string.h>
using namespace std;
const int maxn=5005;
bool map[maxn][maxn];
int low[maxn],dfn[maxn];
int d_cnt;
int n,m;
int in[maxn];
void dfs(int u,int pre)
{
    low[u]=dfn[u]= ++d_cnt;
    for(int i=1;i<=n;++i)
    {
        if(map[u][i]==0) continue;
        if(!dfn[i])
        {
            dfs(i,u);
            low[u]=min(low[i],low[u]);
        }
        else if(pre!=i)
        {
            low[u]=min(low[u],dfn[i]);
        }
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    memset(map,0,sizeof(map));
    int u,v;
    for(int i=1;i<=m;++i)
    {
        scanf("%d%d",&u,&v);
        map[u][v]=1; map[v][u]=1;
    }
    memset(in,0,sizeof(in));
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    d_cnt=0;
    for(int i=1;i<=n;++i) if(!dfn[i]) dfs(i,i);
    for(int i=1;i<=n;++i)
    {
        for(int j=1;j<=n;++j)
        {
            if(!map[i][j]) continue;
            if(i==j) continue;
            if(low[i]!=low[j])
            {
                in[low[j]]++;
            }
        }
    }
    int temp=0;
    for(int i=1;i<=n;++i)
    {
        if(in[i]==1) temp++;
    }
    printf("%d\n",(temp+1)/2);
    return 0;
}









發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章