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;
}









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