HDU 3749 Financial Crisis 经济危机(并查集,割点,双连通分量)

题意:给一个图n个点m条边(不一定连通),接下来又q个询问,询问两个点是为“不相连”,“仅有一条路径可达”,“有两条及以上的不同路径可达”三种情况中的哪一种。注:两条以上的路径指的是路径上的点连1个点也不重复。

思路:并查集+tarjan求割点。

  (1)情况一:先并查集处理,如果两个点从一开始就不连通,直接输出zero

  (2)情况二和情况三:两点既然连通,那么可能是只有1条路径,比如中间隔着一个割点;也可能有多条路径,比如在同一个双连通分量内。那么直接判断其是否在同一个双连通分量内即可,若在同一个双连通分量内,那么路径起码有2条不重复的。

  并查集算法看其他题目。

  tarjan用刘汝佳那个版本。只是我用了unordered_set来存储双连通分量,对于每个询问就在每个双连通分量中查是否同时存在(因为其中一个可能是割点),若同时存在且size>2才是输出 two or more。

  具体实现看代码。

  1 #include <bits/stdc++.h>
  2 using namespace std;
  3 const int N=5000+5;
  4 stack< pair<int,int> >  stac;   //tarjan用的栈
  5 vector<int> vect[N];            //图
  6 unordered_set<int>  bi[N];      //双连通分量
  7
  8 int pre[N];            //并查集用的,保存领导
  9
 10 int find(int x)        //查
 11 {
 12     return pre[x]==x? x: pre[x]=find(pre[x]);
 13 }
 14
 15 void joint(int x,int y)  //并
 16 {
 17     x=find(x);
 18     y=find(y);
 19     if(x!=y)    pre[x]=y;
 20 }
 21
 22
 23 int dfn[N], low[N];
 24 int bccno[N];
 25 int cnter, bcc_cnt;
 26
 27 void DFS(int x, int far)
 28 {
 29     dfn[x]= low[x]= ++cnter;
 30     int chd=0;  //孩子
 31     for(int i=0; i<vect[x].size(); i++)
 32     {
 33         int t=vect[x][i];
 34
 35         if(!dfn[t]) //树边
 36         {
 37             chd++;
 38             stac.push(make_pair(x,t));      //进栈
 39             DFS(t,x);
 40             low[x]=min(low[x],low[t]);
 41             //if((far&&low[t]>=dfn[x]) || (!far&&chd>1) )      //割点
 42             if(low[t]>=dfn[x] )      //割点。如果根只有0或1个孩子呢?那根就不是连通分量中的一个点。
 43             {
 44                 bi[++bcc_cnt].clear(); //连通分量
 45                 while(1)
 46                 {
 47                     int a=stac.top().first;
 48                     int b=stac.top().second;
 49                     stac.pop();
 50                     if(bccno[a]!=bcc_cnt)
 51                     {
 52                         bccno[a]=bcc_cnt;
 53                         bi[bcc_cnt].insert(a);
 54                     }
 55                     if(bccno[b]!=bcc_cnt)
 56                     {
 57                         bccno[b]=bcc_cnt;
 58                         bi[bcc_cnt].insert(b);
 59                     }
 60                     if(a==x && b==t)  break;
 61                 }
 62             }
 63         }
 64         else if(dfn[t]<dfn[x] && t!=far)
 65         {
 66             stac.push(make_pair(x,t));      //进栈
 67             low[x]=min(low[x],dfn[t]);
 68         }
 69     }
 70 }
 71
 72 int cal_bcc(int n)
 73 {
 74     memset(bccno,0,sizeof(bccno));
 75     memset(dfn,0,sizeof(dfn));
 76     memset(low,0,sizeof(low));
 77
 78     while(!stac.empty())    stac.pop();
 79
 80     bcc_cnt= cnter=0;
 81     for(int i=n; i>0; i--)
 82         if(!dfn[i])    DFS(i,0);
 83 }
 84
 85
 86 int check(int n,int a, int b)
 87 {
 88     if(find(a)!=find(b))    return 0;   //不连通
 89
 90     for(int i=1; i<=bcc_cnt; i++)
 91     {
 92         if(bi[i].find(a)!=bi[i].end() && bi[i].find(b)!=bi[i].end() && bi[i].size()>2)  //对于只有两个点的双连通分量,仅有1条边,输出one。
 93         {
 94             printf("two or more\n");
 95             return 1;
 96         }
 97     }
 98     printf("one\n");
 99     return 1;
100 }
101
102 void init(int n)
103 {
104     memset(pre,0,sizeof(pre));
105     for(int i=1; i<=n; i++) vect[i].clear();
106     for(int i=1; i<=n; i++) pre[i]=i;
107 }
108
109 int main()
110 {
111     freopen("input.txt", "r", stdin);
112     int n, m, q, a, b, j=0;
113     while(scanf("%d%d%d",&n, &m, &q), n+m+q)
114     {
115         init(n);
116         for(int i=0; i<m; i++)
117         {
118             scanf("%d%d", &a, &b);
119             vect[++a].push_back(++b);
120             vect[b].push_back(a);
121             joint(a,b);             //并查集
122         }
123
124         cal_bcc(n);                 //tarjan算法
125         printf("Case %d:\n",++j);
126 /*
127         for(int i=1; i<=bcc_cnt; i++)     //输出每个双连通分量。
128         {
129             printf("第%d个双连通分量包含以下点:",i);
130             for(unordered_set<int>::iterator it=bi[i].begin(); it!=bi[i].end();it++)
131             {
132                 printf("%d ",*it);
133             }
134             printf("\n");
135         }
136 */
137         for(int i=0; i<q; i++)
138         {
139             scanf("%d%d", &a, &b);
140             if(!check(n, ++a, ++b)) printf("zero\n");
141         }
142     }
143     return 0;
144 }

AC代码

  

  

时间: 2024-10-29 19:13:32

HDU 3749 Financial Crisis 经济危机(并查集,割点,双连通分量)的相关文章

HDU 3749 Financial Crisis

Financial Crisis 题意:给一个图,包含N ( 3 <= N <= 5000 )个点, M ( 0 <= M <= 10000 )条边 and Q ( 1 <= Q <= 1000 )次查询.查询:两个点是否是点-双连通: 点-双连通:两点至少存在两条"点不重复"的路径:简称双连通(biconnected); 思路:直接调用dfs求割点的算法,其实也是Tarjan发明的,就是在判断出一个割点之后,就把栈S中该双连通分量的所有点(就在栈顶

HDU 3635 延缓更新的并查集

Dragon Balls Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 2839    Accepted Submission(s): 1097 Problem Description Five hundred years later, the number of dragon balls will increase unexpecte

HDU 3461 Code Lock(并查集的应用+快速幂)

* 65536kb,只能开到1.76*10^7大小的数组.而题目的N取到了10^7,我开始做的时候没注意,用了按秩合并,uset+rank达到了2*10^7所以MLE,所以貌似不能用按秩合并. 其实路径压缩也可以不用.............  题目的大意: 一个密码锁上有编号为1到N的N个字母,每个字母可以取26个小写英文字母中的一个.再给你M个区间[L,M],表示该区间的字母可以一起同步"增加"(从'a'变为'b'为增1,'z'增1为'a').假如一组密码按照给定的区间进行有限

HDU 1558 Segment set (并查集+线段非规范相交)

题目链接 题意 : 如果两个线段相交就属于同一集合,查询某条线段所属集合有多少线段,输出. 思路 : 先判断与其他线段是否相交,然后合并. 1 //1558 2 #include <cstdio> 3 #include <cstring> 4 #include <iostream> 5 #include <cmath> 6 #define eps 1e-8 7 #define zero(x) (((x) > 0 ? (x) : (-x)) < e

hdu 4587 2013南京邀请赛B题/ / 求割点后连通分量数变形。

题意:求一个无向图的,去掉两个不同的点后最多有几个连通分量. 思路:枚举每个点,假设去掉该点,然后对图求割点后连通分量数,更新最大的即可.算法相对简单,但是注意几个细节: 1:原图可能不连通. 2:有的连通分量只有一个点,当舍去该点时候,连通分量-1: 复习求割点的好题! #include<iostream> #include<cstdio> #include<vector> using namespace std; int n,m; vector<vector&

HDU 3407.Zjnu Stadium 加权并查集

Zjnu Stadium Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 3726    Accepted Submission(s): 1415 Problem Description In 12th Zhejiang College Students Games 2007, there was a new stadium built

HDU 1213 How Many Tables (并查集)

How Many Tables Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Submit Status Practice HDU 1213 Appoint description:  System Crawler  (2015-05-25) Description Today is Ignatius' birthday. He invites a lot of friends. Now

hdu 1232 畅通工程(并查集算法)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1232 畅通工程 Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 31088    Accepted Submission(s): 16354 Problem Description 某省调查城镇交通状况,得到现有城镇道路统计表,表中列出了每条

HDU 3371 Connect the Cities(并查集+Kruskal)

题目网址:http://acm.hdu.edu.cn/showproblem.php?pid=3371 思路: 这道题很明显是一道最小生成树的题目,有点意思的是,它事先已经让几个点联通了.正是因为它先联通了几个点,所以为了判断连通性 很容易想到用并查集+kruskal. 不过要注意 这题有一个坑点,就是边数很多 上限是25000,排序的话可能就超时了.而点数则比较少 上限是500,所以很多人选择用Prim做.但我个人觉得这样连通性不好判断.其实边数多没关系,我们主要去重就好啦,用邻接矩阵存下两点