2020/5/10的一篇题解【高手训练】【强联通分量】魔法石

题面

样例

 

样例输入

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

5 6
1 2 0
2 3 0
3 1 0
3 4 0
4 5 1
5 3 0
1 2

样例输出

YES
NO
YES

 这题我调了好久,码力还是一如既往的不行。

直接开始吧。
其实对于这题,它如果想变成一道简单题,只需要把每条路只能行走一次这个条件去掉,而且它与寻找魔法石的要求结合在一起,使人更是难以去解开。
因此,大致的思路也就是解决掉这个麻烦的设定,甚至让它成为我们解题 的方法之一。
因为只能行走一次其实就是走完后把这条边删去,如果这个图因为删去这条边而变得不连通,就不能回来了。
这是什么?这就是割边的定义:如果一个图因为删掉了一条边而边的不连通,我们把这条边称为割边。
所以我们如果走过的是一个割边,就不能回来了。

易证:
我们是一定不会经过一条同样的边且方向一样的走过两遍的。
如果我走过了一条同样的边,我可以证明出这肯定是走过了一个环到达了同样的点,完全可以不走这样的一个环而去另一条边。


可以发现一个点到另一个点如果有两条路径,那么,我可以认为这两个点是同一个点。我从其中一个点出发,能够到达另一个点而且并不会影响到我去这两个点之外的另一个点。
如果我不返回其中的一个点,而是在到达另一个点以后从另一条边前往其他的点,也是可以的。
因为这肯定是在所属边双的两条简单路径上离开的,我只需要通过另一条边到达这两个节点中的另一个节点,在走那一条边前往这两个节点之外的想去的节点就好了。

所以我只需要判断能不能到达一个含有魔法石的边双就能知道是否能拿到魔法石了

事实上,如果两个点不属于同一个边双上的,那么肯定就是要有去无回的。但是我们可以有去无回的情况就只有一个——从起点到终点。
那么我们在他们之间连一条代价为零的边,以此来解决这个特例,再判断他们是否在同一个边双中。如果不在,就说明他们在原图中是不连通的。这也是为了解决关于魔法石的问题而做的。

到最后,我们只需要把起点和终点连接起来,判断在起点终点所在的边双中是否有一个魔法石就说明是否能拿到了。qwq 简单题

举个栗子
对于这样的一个图,我要从1节点到2节点

 

 

 那么就可以在1 2之间连接一条代价为零的边,判断之后发现,1 2 3在同一个边双之内,而且这里面是没有魔法石的。
如果我去前往4 5 6这个边双之中取魔法石,就不能回来了。3 4是一个割边。
4 5 6这个边双中,我们只要到达其中的任意一个节点,就能够拿到魔法石并且回来。

对于这样的图

 

 连接1 2,这个整个图就是一个边双,所以是一定能拿到魔法石的。

 code

#include<bits/stdc++.h>
using namespace std;

const int A=300005;

int t;
int n,m;
int u;
int begin,end;

struct gg
{
    int to;
    bool s;
};

gg a;

vector <gg> ljb1[A];

int dfn[A],low[A],tim,num,belong[A];
bool ex[A];

stack <int> st;

void tarjan(int fa,int x)
{
    dfn[x]=low[x]=++tim;
    st.push(x);
    ex[x]=1;
    for(int y=0;y<ljb1[x].size();y++)
    {
        gg z=ljb1[x][y];
        if(z.to==fa)    continue;
        if(!dfn[z.to])
        {
            tarjan(x,z.to);
            low[x]=min(low[x],low[z.to]);
        }
        else
            if(ex[z.to])
                low[x]=min(low[x],dfn[z.to]);
    }
    if(dfn[x]==low[x])
    {
        int y=st.top();
        num++;
        while(y!=x)
        {
            ex[y]=0;
            belong[y]=num;
            st.pop();
            y=st.top();
        }
        ex[x]=0;
        belong[x]=num;
        st.pop();
    }
}

vector <gg> ljb2[A];
bool stone[A],vis[A],yes;
int all;

void bfs(int x)
{
    if(yes==1)    return;
    if(vis[x]==1)    return;
    vis[x]=1;
    if(x==belong[end])
    {
        if(all>0)    yes=1;
        return;
    }
    for(int y=0;y<ljb2[x].size();y++)
    {
        gg z=ljb2[x][y];
        if(z.s==1||stone[z.to]==1)    all++;
        bfs(z.to);
        if(z.s==1||stone[z.to]==1)    all--;
    }
}

void clean()
{
    for(int i=1;i<=n;i++)
        ljb1[i].clear();
    for(int i=1;i<=num;i++)
        ljb2[i].clear();
    tim=num=yes=all=0;
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(belong,0,sizeof(belong));
    memset(ex,0,sizeof(ex));
    memset(stone,0,sizeof(stone));
    memset(vis,0,sizeof(vis));
    while(!st.empty())
        st.pop();
}

int main()
{
    scanf("%d",&t);
    while(t--)
    {
        clean();
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d%d",&u,&a.to,&a.s);
            ljb1[u].push_back(a);
            swap(u,a.to);
            ljb1[u].push_back(a);
        }
        scanf("%d%d",&begin,&end);
        for(int i=1;i<=n;i++)
            if(!dfn[i])    tarjan(0,i);
        for(int i=1;i<=n;i++)
            for(int y=0;y<ljb1[i].size();y++)
            {
                gg z=ljb1[i][y];
                if(belong[i]!=belong[z.to])
                {
                    a.to=belong[z.to];
                    if(z.s==1)
                        a.s=1;
                    else
                        a.s=0;
                    ljb2[belong[i]].push_back(a);
                }
                if(belong[i]==belong[z.to]&&z.s==1)
                    stone[belong[i]]=1;
            }
        if(stone[belong[begin]]==1)    all++;
        bfs(belong[begin]);
        if(yes==1)    printf("YES\n");
        else    printf("NO\n");
    }
    return 0;
}

 

The end

posted @ 2020-05-11 09:21  HL_ZZP  阅读(116)  评论(1编辑  收藏  举报