题意:
给一个图n个点m条边(不一定连通),接下来又q个询问,询问两个点是为“不相连”,“仅有一条路径可达”,“有两条及以上的不同路径可达”三种情况中的哪一种。注:两条以上的路径指的是路径上的点连1个点也不重复。
思路:并查集+tarjan求割点。
(1)情况一:先并查集处理,如果两个点从一开始就不连通,直接输出zero
(2)情况二和情况三:两点既然连通,那么可能是只有1条路径,比如中间隔着一个割点;也可能有多条路径,比如在同一个双连通分量内。那么直接判断其是否在同一个双连通分量内即可,若在同一个双连通分量内,那么路径起码有2条不重复的。
并查集算法看其他题目。
tarjan用刘汝佳那个版本。只是我用了unordered_set来存储双连通分量,对于每个询问就在每个双连通分量中查是否同时存在(因为其中一个可能是割点),若同时存在且size>2才是输出 two or more。
具体实现看代码。
#include <bits/stdc++.h>
using namespace std;
const int N=+;
stack< pair<int,int> > stac; //tarjan用的栈
vector<int> vect[N]; //图
unordered_set<int> bi[N]; //双连通分量 int pre[N]; //并查集用的,保存领导 int find(int x) //查
{
return pre[x]==x? x: pre[x]=find(pre[x]);
} void joint(int x,int y) //并
{
x=find(x);
y=find(y);
if(x!=y) pre[x]=y;
} int dfn[N], low[N];
int bccno[N];
int cnter, bcc_cnt; void DFS(int x, int far)
{
dfn[x]= low[x]= ++cnter;
int chd=; //孩子
for(int i=; i<vect[x].size(); i++)
{
int t=vect[x][i]; if(!dfn[t]) //树边
{
chd++;
stac.push(make_pair(x,t)); //进栈
DFS(t,x);
low[x]=min(low[x],low[t]);
//if((far&&low[t]>=dfn[x]) || (!far&&chd>1) ) //割点
if(low[t]>=dfn[x] ) //割点。如果根只有0或1个孩子呢?那根就不是连通分量中的一个点。
{
bi[++bcc_cnt].clear(); //连通分量
while()
{
int a=stac.top().first;
int b=stac.top().second;
stac.pop();
if(bccno[a]!=bcc_cnt)
{
bccno[a]=bcc_cnt;
bi[bcc_cnt].insert(a);
}
if(bccno[b]!=bcc_cnt)
{
bccno[b]=bcc_cnt;
bi[bcc_cnt].insert(b);
}
if(a==x && b==t) break;
}
}
}
else if(dfn[t]<dfn[x] && t!=far)
{
stac.push(make_pair(x,t)); //进栈
low[x]=min(low[x],dfn[t]);
}
}
} int cal_bcc(int n)
{
memset(bccno,,sizeof(bccno));
memset(dfn,,sizeof(dfn));
memset(low,,sizeof(low)); while(!stac.empty()) stac.pop(); bcc_cnt= cnter=;
for(int i=n; i>; i--)
if(!dfn[i]) DFS(i,);
} int check(int n,int a, int b)
{
if(find(a)!=find(b)) return ; //不连通 for(int i=; i<=bcc_cnt; i++)
{
if(bi[i].find(a)!=bi[i].end() && bi[i].find(b)!=bi[i].end() && bi[i].size()>) //对于只有两个点的双连通分量,仅有1条边,输出one。
{
printf("two or more\n");
return ;
}
}
printf("one\n");
return ;
} void init(int n)
{
memset(pre,,sizeof(pre));
for(int i=; i<=n; i++) vect[i].clear();
for(int i=; i<=n; i++) pre[i]=i;
} int main()
{
freopen("input.txt", "r", stdin);
int n, m, q, a, b, j=;
while(scanf("%d%d%d",&n, &m, &q), n+m+q)
{
init(n);
for(int i=; i<m; i++)
{
scanf("%d%d", &a, &b);
vect[++a].push_back(++b);
vect[b].push_back(a);
joint(a,b); //并查集
} cal_bcc(n); //tarjan算法
printf("Case %d:\n",++j);
/*
for(int i=1; i<=bcc_cnt; i++) //输出每个双连通分量。
{
printf("第%d个双连通分量包含以下点:",i);
for(unordered_set<int>::iterator it=bi[i].begin(); it!=bi[i].end();it++)
{
printf("%d ",*it);
}
printf("\n");
}
*/
for(int i=; i<q; i++)
{
scanf("%d%d", &a, &b);
if(!check(n, ++a, ++b)) printf("zero\n");
}
}
return ;
}
AC代码