[noip模拟赛]Formula 1(bfs+并查集)

题目描述

[noip模拟赛]Formula 1(bfs+并查集)_第1张图片
[noip模拟赛]Formula 1(bfs+并查集)_第2张图片

题解

听ShallWe讲了之后觉得非常厉害呀。
首先,在每两个有边的点中间都加一个点,每条边的边权都是1,然后将s、t、所有的加油站都加到一个队列里,做广搜。广搜的时候每次都将边的始点和终点用并查集并起来,直到s和t连通,当前搜到的点的dis就是答案。
考虑一下这样为什么是对的。基于广搜的性质搜到的每一个点的dis一定是到最近的加油站的距离,而同时当s和t恰好连通时当前搜到的dis一定是这些里面的最大值。这样就非常巧妙地保证了正确性。
同时拆点的思路也非常厉害,可以形象化地理解为两个点碰到了中间。

代码

#include
#include
#include
#include
using namespace std;
#define N 500005
#define M 150005

int T,n,m,k,x,y,z,s,t,ans;
int tot,point[N],nxt[M*4],v[M*4];
int loc[N],dis[N],f[N];
bool vis[N];
queue <int> q;

inline void clear()
{
    n=m=k=x=y=z=s=t=ans=tot=0;
    memset(point,0,sizeof(point)); memset(nxt,0,sizeof(nxt)); memset(v,0,sizeof(v));
    memset(loc,0,sizeof(loc)); memset(dis,0,sizeof(dis)); memset(f,0,sizeof(f)); memset(vis,0,sizeof(vis));
    while (!q.empty()) q.pop();
}
inline void addedge(int x,int y)
{
    ++tot; nxt[tot]=point[x]; point[x]=tot; v[tot]=y;
    ++tot; nxt[tot]=point[y]; point[y]=tot; v[tot]=x;
}
inline int find(int x)
{
    if (x==f[x]) return x;
    f[x]=find(f[x]);
    return f[x];
}
inline void merge(int x,int y)
{
    int f1=find(x),f2=find(y);
    f[f1]=f2;
}
inline int bfs()
{
    while (!q.empty())
    {
        int now=q.front(); q.pop();
        for (int i=point[now];i;i=nxt[i])
        {
            merge(now,v[i]);
            if (find(s)==find(t))
                return dis[v[i]];
            if (!vis[v[i]])
            {
                dis[v[i]]=dis[now]+1;
                vis[v[i]]=true;
                q.push(v[i]);
            }
        }
    }
    return -1;
}
int main()
{
    freopen("f1.in","r",stdin);
    freopen("f1.out","w",stdout);
    scanf("%d",&T);
    while (T--)
    {
        clear();

        scanf("%d%d%d",&n,&m,&k);
        z=n;
        for (int i=1;i<=k;++i) scanf("%d",&loc[i]);
        for (int i=1;i<=m;++i)
        {
            scanf("%d%d",&x,&y);
            z++;
            addedge(x,z); addedge(y,z);
        }
        scanf("%d%d",&s,&t);

        for (int i=1;i<=z;++i) f[i]=i;
        if (!vis[s]) vis[s]=true,q.push(s);
        if (!vis[t]) vis[t]=true,q.push(t);
        for (int i=1;i<=k;++i)
            if (!vis[loc[i]])
            {
                vis[loc[i]]=true;
                q.push(loc[i]);
            }

        ans=bfs();
        printf("%d\n",ans);
    }
}

总结

这道题的拆点非常厉害呀。

你可能感兴趣的:(题解,搜索,并查集)