ZOJ 4097 Rescue the Princess(边双连通分量缩点 + LCA)_ZOJ

ZOJ 4097 Rescue the Princess(边双连通分量缩点 + LCA)_ZOJ_02

 

 

大致题意:给你一个可以有重边和自环的n个点m条边的无向图,给你q个询问。每个询问告诉你公主的位置和两个骑士的位置。现在骑士要去救公主,问是否存在从两个骑士出发点到公主的不相交路径。

我已经标出来了,是边不相交,而不是点不相交。然后这是一个无向图,所以我们容易想到的第一件事情就是要进行缩点。关于缩点,这里用到的是边双连通分量的定义,至于点双连通分量、边双连通分量和连通分量的区别,我会在 ​​另外一篇博客 ​​里面具体讲。

缩完点之后,原本的图就变成了一棵树或者一个森林,在树上通过LCA来判断两个骑士与公主的关系。首先,如果有一个骑士与公主不在同一棵树上,那么显然答案是No。然后如果有一个骑士与公主在同一个缩点里面,那么显然一定是Yes,因为缩点内部肯定构成环。再然后,如果两个骑士在同一个缩点,而且公主不在这个缩点,那么答案一定是No,因为这两个骑士一定要走出这个缩点往公主所在缩点走,而这样的路径只存在一条。

最后,排除完以上三种情况之后,我们就要用LCA来判断。考虑当能够找到两条路径的时候,公主一定要在两个骑士的树上路径之间,那么问题就变成了判断第三个点是否在前两个点的路径之间。两个骑士的LCA的情况分类讨论一下,你会发现,三个点中,任意两个点求LCA。公主与两个骑士的LCA记为lca1和lca2,两个骑士的LCA记为lca3。如果要满足条件,lca1和lca2中一定有一个等于lca3,而且不等于的那个lca一定是公主本身。具体操作见代码:

#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
#define LL long long
#define sc(x) scanf("%d",&x)
#define scc(x,y) scanf("%d%d",&x,&y)
#define sccc(x,y,z) scanf("%d%d%d",&x,&y,&z)

using namespace std;

const int N = 1e5 + 10;

int dfn[N],low[N],belong[N],rt[N];
int g[N<<2],ls[N],nxt[N<<2],e;
typedef pair<int,int> P;
int n,m,q,dindex,cnt;
vector<int> RT;
stack<int> sta;
bool v[N];

namespace LCA
{
int dp[N][20],dep[N];
vector<int> g[N];

inline void init()
{
memset(g,0,sizeof(g));
}

inline void dfs(int x,int fa)
{
for(int i=0;i<g[x].size();i++)
{
int y=g[x][i];
if (y==fa) continue;
dep[y]=dep[x]+1;
dp[y][0]=x; dfs(y,x);
}
}

void ST()
{
for(int j=0;j<17;j++)
for(int i=1;i<=cnt;i++)
dp[i][j+1]=dp[dp[i][j]][j];
}

inline int lca(int u,int v)
{
if (u==v) return u;
if (dep[v]>dep[u]) swap(u,v);
for(int i=18;i>=0;i--)
if (dep[dp[u][i]]>=dep[v]) u=dp[u][i];
if (u==v) return u;
for(int i=18;i>=0;i--)
if (dp[u][i]!=dp[v][i]) u=dp[u][i],v=dp[v][i];
return dp[u][0];
}
}

inline void addedge(int x,int y)
{
g[e]=y; nxt[e]=ls[x]; ls[x]=e++;
g[e]=x; nxt[e]=ls[y]; ls[y]=e++;
}

inline void tarjan(int x,int fa,int root)
{
rt[x]=root; int t=0;
v[x]=1; sta.push(x);
low[x]=dfn[x]=++dindex;
for(int i=ls[x];~i;i=nxt[i])
{
int y=g[i];
if (y==fa&&!t){t++;continue;}
if (!dfn[y])
{
tarjan(y,x,root); low[x]=min(low[x],low[y]);
} else if (v[y]) low[x]=min(low[x],dfn[y]);
}
if (dfn[x]==low[x])
{
cnt++; int j=0;
do
{
j=sta.top();sta.pop();
v[j]=0; belong[j]=cnt;
} while (j!=x);
}
}

inline void init()
{
RT.clear();
LCA::init();
e=dindex=cnt=0;
memset(ls,-1,sizeof(ls));
memset(dfn,0,sizeof(dfn));
}

int main()
{
int T; sc(T);
while(T--)
{
init();
sccc(n,m,q);
for(int i=1;i<=m;i++)
{
int x,y;
scc(x,y);
if (x==y) continue;
addedge(x,y);
}
for(int i=1;i<=n;i++)
if (!dfn[i]) RT.push_back(i),tarjan(i,-1,i);
for(int i=1;i<=n;i++)
{
int x=belong[i];
for(int j=ls[i];~j;j=nxt[j])
{
int y=belong[g[j]];
if (y==x) continue;
LCA::g[x].push_back(y);
}
}
for(int i=0;i<RT.size();i++)
LCA::dfs(belong[RT[i]],-1);
LCA::ST();
while(q--)
{
int x,y,z; sccc(x,y,z);
if (rt[x]!=rt[y]||rt[x]!=rt[z])
{
puts("No"); continue;
}
x=belong[x];
y=belong[y];
z=belong[z];
if (x==y||x==z)
{
puts("Yes"); continue;
}
if (y==z)
{
puts("No"); continue;
}
int lca1=LCA::lca(x,y);
int lca2=LCA::lca(x,z);
int lca3=LCA::lca(y,z);
if (lca3==lca1&&lca2==x||lca3==lca2&&lca1==x) puts("Yes");
else puts("No");


}
}

}