【LCA+tarjan缩点+无向图含重边缩点板子】ZOJ Problem Set - 4097 Rescue the Princessing

Rescue the Princess


Time Limit: 1 Second      Memory Limit: 65536 KB


Princess Cjb is caught by Heltion again! Her knights Little Sub and Little Potato are going to Heltion Kingdom to rescue her.

Heltion Kingdom is composed of  islands, numbered from  to . There are  bridges in the kingdom, among which the -th bridge connects the -th island and the -th island. The knights can go through each bridge in both directions.

Landing separately on the -th and the -th island, the two knights start their journey heading to the -th island where the princess is imprisoned. However, as the knights are fat and the bridges are unstable, there will be a risk of breaking down the bridge and falling into the water if they go through one or more common bridges during their journey.

Thus, to successfully bring back the princess, two paths \textbf{with no common bridges} are needed: one starts from the -th island and leads to the -th island, while the other starts from the -th island and also leads to the -th island.

As the princess is caught very often, the knights will ask you for help  times. Each time, given their starting islands and their goal, you need to tell them whether it's possible to find two paths satisfying the constraints above.

Input

There are multiple test cases. The first line of the input contains an integer , indicating the number of test cases. For each test case:

The first line contains three integers ,  and  (, , ), indicating the number of islands, the number of bridges and the number of queries.

The following  lines describe the bridges. The -th line contains two integers  and  (), indicating the two islands the -th bridge connects. Notice that different bridges may connect the same pair of islands and a bridge may connect an island to itself.

The following  lines describe the queries. The -th line contains three integers ,  and  (), indicating the island where the princess is imprisoned and the starting islands of the two knights.

It's guaranteed that the sum of  of all test cases will not exceed , the sum of  of all test cases will not exceed , and the sum of  of all test cases will not exceed .

Output

For each test case output  lines indicating the answers of the queries. For each query, if two paths meeting the constraints can be found, output "Yes" (without quotes), otherwise output "No" (without quotes).

Sample Input

2
6 7 4
1 2
2 3
3 1
4 5
5 6
6 4
1 4
4 1 3
1 4 2
1 2 3
1 3 3
2 1 2
1 2
1 1 1
2 1 2

Sample Output

No
Yes
Yes
Yes
Yes
Yes

Hint

For the first sample test case:

  • For the 2nd query, we can select the paths 4-1 and 2-1.
  • For the 3rd query, we can select the paths 2-1 and 3-1.
  • For the 4th query, we can select the paths 3-1 and 3-2-1.

For the second sample test case:

  • For the 1st query, as the knights and the princess are on the same island initially, the answer is "Yes".
  • For the 2nd query, as one of the knights are on the same island with the princess initially, he does not need to cross any bridge. The other knight can go from island 1 to island 2 directly.

题意:

有n个点,m条无向边,每次Q组询问

每组询问给你三个点:x,y,z

问你点y到点x和点z到点x能不能走到且两条路径上没有重边

可以输出Yes,否则No

思路:

先tarjan缩点,建新图

预处理lca后

进行分类讨论

1、三个点在缩点建边后、应在一棵树上

2、三个点要么存在骑士和公主在同一个点的情况,要么公主在两名骑士组成的简单路径中间

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
int head[maxn],to[4*maxn],nxt[4*maxn];
int dfn[maxn],low[maxn],fa[maxn];
bool vis[maxn];
stack <int> P;
int n,m,Q,cnt,Top,tol;

int dis[maxn],dep[maxn],f[maxn][21],pre[maxn];
vector <int> G[maxn];

void tarjan(int v,int Fa)
{
    dfn[v]=low[v]=++tol;
    P.push(v);
    bool fir=1;
    for(int i=head[v];i!=-1;i=nxt[i])
    {
        int u=to[i];
        if(u==Fa&&fir)
        {
            fir=0;
            continue;
        }
        if(!dfn[u])
        {
            tarjan(u,v);
            low[v]=min(low[v],low[u]);
        }
        else if(!vis[u])
        {
            low[v]=min(low[v],dfn[u]);
        }
    }
    if(low[v]==dfn[v])
    {
        Top++;
        int s;
        do{
            s=P.top();
            P.pop();
            fa[s]=Top;
            vis[s]=1;
        }while(s!=v);
    }
}

void add_edge(int u,int v)
{
    to[++cnt]=v;
    nxt[cnt]=head[u];
    head[u]=cnt;

    to[++cnt]=u;
    nxt[cnt]=head[v];
    head[v]=cnt;
}

void init1()
{
    for(int i=1; i<=n; i++)
    {
        pre[i]=i;
        dis[i]=dep[i]=vis[i]=0;
        for(int j=0;j<=20;j++)
            f[i][j]=0;
        G[i].clear();
    }
}


void init()
{
    for(int i=1;i<=n;i++)
        head[i]=-1;
    cnt=0;
}

void Init_tarjan()
{
    Top=tol=0;
    for(int i=1; i<=n; i++)
    {
        low[i]= dfn[i]= fa[i]= vis[i]=0;
    }
}

int finding(int x)
{
    if(pre[x]==x) return x;
    return pre[x]=finding(pre[x]);
}

void unit(int x,int y)
{
    int fx=finding(x),fy=finding(y);
    if(fx!=fy) pre[fx]=fy;
}

void dfs(int u,int fa,int deep)
{
    vis[u]=1;
    f[u][0]=fa;
    dep[u]=deep;
    for(int i=0; i<G[u].size(); i++)
    {
        int v=G[u][i];
        if(v==fa||vis[v]) continue;
        dis[v]=dis[u]+1;
        dfs(v,u,deep+1);
    }
}

void init_lca()
{
    for(int i=1; i<=n; i++)
    {
        if(!vis[i])
        {
            dis[i]=0;
            dfs(i,-1,0);
        }
    }

    for(int j=1; (1<<j)<=n; j++)
    {
        for(int i=1; i<=n; i++)
        {
            f[i][j]=f[f[i][j-1]][j-1];
        }
    }
}



int lca(int a,int b)
{
    if(dep[a]<dep[b]) swap(a,b);
    int d=dep[a]-dep[b];
    for(int i=0;(1<<i)<=d;i++)
    {
        if((1<<i)&d) a=f[a][i];
    }

    if(a==b) return a;
    for(int i=20;i>=0;i--)
    {
        if(f[a][i]!=f[b][i])
        {
            a=f[a][i];
            b=f[b][i];
        }
    }
    return f[a][0];
}


int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d%d%d",&n,&m,&Q);
        init();
        Init_tarjan(); //初始化tarjan
        for(int i=1;i<=m;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            add_edge(u,v);
        }

        for(int i=1;i<=n;i++)
        {
            if(!dfn[i]) tarjan(i,0);
        }

        init1();  //初始化lca
        for(int i=1;i<=n;i++)
        {
            for(int j=head[i];j!=-1;j=nxt[j])
            {
                int u=i,v=to[j];
                if(fa[u]==fa[v]) continue;
                G[fa[u]].push_back(fa[v]); //建缩点后的图
                G[fa[v]].push_back(fa[u]);
                unit(fa[u],fa[v]);  //并查集,pre[]是缩点后建的图的点在哪棵树上
            }
        }

        init_lca();  //预处理lca


        while(Q--)
        {
            int x,y,z;
            scanf("%d%d%d",&x,&y,&z);
            x=fa[x],y=fa[y],z=fa[z];  //得到缩点后实际建图的点
            if(finding(x)==finding(y)&&finding(x)==finding(z))  //都在一棵树上
            {
                if(x==y||x==z)  //有一个骑士和公主在一个缩点里,就是yes
                    printf("Yes\n");
                else //否则一棵树上,两名骑士到公主的路径不相同,就需要,公主在两名骑士组成的简单路径中间
                {
                    int l1=dis[y]+dis[z]-2*dis[lca(y,z)]; 
                    int l2=dis[x]+dis[y]-2*dis[lca(x,y)];
                    int l3=dis[x]+dis[z]-2*dis[lca(x,z)];
                    if(l1==l2+l3)
                        printf("Yes\n");
                    else printf("No\n");
                }
            }
            else printf("No\n");
        }
    }

    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_41037114/article/details/89363413
今日推荐