[双连通分量 缩点 并查集] POJ 3177 Redundant Paths & 3352 Road Construction

双连通分量缩点 并查集 竟然是第一次打

缩点后是一棵树

结论是(叶子节点+1)>>1 

也就是每次取lca最浅的两个叶子连边


#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#define cl(x) memset(x,0,sizeof(x))
using namespace std;

inline char nc(){
  static char buf[100000],*p1=buf,*p2=buf;
  if (p1==p2) { p2=(p1=buf)+fread(buf,1,100000,stdin); if (p1==p2) return EOF; }
  return *p1++;
}

inline int read(int &x){
  char c=nc(),b=1;
  for (;!(c>='0' && c<='9');c=nc()) if (c=='-') b=-1; else if (c==EOF) return 0;
  for (x=0;c>='0' && c<='9';x=x*10+c-'0',c=nc()); x*=b; return 1;
}

const int N=5005;
const int M=10005;

struct edge{
  int u,v,next;
}G[M<<1];
int head[N],inum=1;

inline void add(int u,int v,int p){
  G[p].u=u; G[p].v=v; G[p].next=head[u]; head[u]=p;
}

int fat[N];
inline void init(int n){
  for (int i=1;i<=n;i++) fat[i]=i;
}
inline int Fat(int u){
  return u==fat[u]?u:fat[u]=Fat(fat[u]);
}

int n,m;
int father[N],vst[N];

#define V G[p].v
inline void dfs(int u,int fa){
  vst[u]=1;
  for (int p=head[u];p;p=G[p].next)
    if (p!=(fa^1)){
      if (!vst[V])
	father[V]=u,dfs(V,p);
      else{
	int tem=u,last=Fat(V),cur;
	while ((cur=Fat(tem))!=last){
	  fat[cur]=Fat(father[cur]);
	  tem=Fat(father[cur]);
	}
      }
    }
}

int deg[N],ans;

int main(){
  int iu,iv;
  freopen("t.in","r",stdin);
  freopen("t.out","w",stdout);
  while (read(n) && read(m)){
    init(n);
    for (int i=1;i<=m;i++)
      read(iu),read(iv),add(iu,iv,++inum),add(iv,iu,++inum);
    dfs(1,0);
    ans=0;
    for (int i=1;i<=n;i++)
	for (int p=head[i];p;p=G[p].next)
	  if (Fat(i)!=Fat(V))
	    deg[Fat(i)]++;
	for (int i=1;i<=n;i++)
	  if (deg[i]==1)
	    ans++;
    ans=(ans+1)>>1;
    printf("%d\n",ans);
    cl(head); inum=1; cl(vst); cl(father);
  }
  return 0;
}

还有不是我写的神奇的scc写法


#include <cstdio>  
#include <iostream>  
#include <cstring>  
#include <cmath>  
#include <algorithm>  
#include <string.h>  
#include <string>  
#include <vector>  
#include <queue>  
  
#define MEM(a,x) memset(a,x,sizeof a)  
#define eps 1e-8  
#define MOD 10009  
#define INF 99999999  
#define ll __int64  
#define bug cout<<"here"<<endl  
#define fread freopen("ceshi.txt","r",stdin)  
#define fwrite freopen("out.txt","w",stdout)  
  
using namespace std;  
  
void read(int &x)  
{  
    char ch;  
    x=0;  
    while(ch=getchar(),ch!=' '&&ch!='\n')  
    {  
        x=x*10+ch-'0';  
    }  
}  
  
const int MAXN=5010;  
const int MAXM=10010;  
struct Edge  
{  
    int to,next;  
}edge[2*MAXM];  
int head[MAXN],tot;  
int low[MAXN],dfn[MAXN],stack[MAXN],belong[MAXN];//belong数组的值是1~scc  
int index,top;  
int scc; //强连通分量的个数  
bool instack[MAXN];  
int num[MAXN];//各个强连通分量包含点个数 数组编号1~scc  
//num数组不一定需要 结合实际情况  
int degree[MAXN];  
  
void addedge(int u,int v)  
{  
    edge[tot].to=v; edge[tot].next=head[u]; head[u]=tot++;  
}  
  
void Tarjan(int u,int fa)  
{  
    low[u]=dfn[u]=++index;  
    stack[++top]=u;  
    instack[u]=1;  
    for(int i=head[u];i!=-1;i=edge[i].next)  
    {  
        int v=edge[i].to;  
        if(i==(fa^1))  continue;  
        if(!dfn[v])  
        {  
            Tarjan(v,i);  
            low[u]=min(low[u],low[v]);  
        }  
        else if(instack[v])  
            low[u]=min(low[u],dfn[v]);  
    }  
    if(dfn[u]==low[u])  
    {  
        scc++;  
        while(1)  
        {  
            int v=stack[top--];  
            instack[v]=0;  
            belong[v]=scc;  
            if(v==u)  
                break;  
        }  
    }  
}  
  
int main()  
{  
//    fread;  
    int n,m;  
    while(scanf("%d%d",&n,&m)!=EOF)  
    {  
        tot=index=top=scc=0;  
        MEM(head,-1);  
        MEM(low,0);  
        MEM(dfn,0);  
        MEM(instack,0);  
        MEM(degree,0);  
        for(int i=0;i<m;i++)  
        {  
            int u,v;  
            scanf("%d%d",&u,&v);  
            addedge(u,v);  
            addedge(v,u);  
        }  
        for(int i=1;i<=n;i++)  
            if(!dfn[i])  
                Tarjan(1,-1);  
        for(int i=1;i<=n;i++)  
        {  
            for(int j=head[i];j!=-1;j=edge[j].next)  
            {  
                int v=edge[j].to;  
                if(belong[i]!=belong[v])  
                    degree[belong[i]]++;  
            }  
        }  
        int sum=0;  
        for(int i=1;i<=n;i++)  
            if(degree[i]==1)  
                sum++;  
        int ans=(sum+1)/2;  
        printf("%d\n",ans);  
    }  
    return 0;  
}  


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值