AtCoder Beginner Contest 350 G - Mediator

链接:https://atcoder.jp/contests/abc350/tasks/abc350_g

大致题意:给出n个点,q个询问
1号询问 要求u,v之前加一条无向边 图始终是一个森林
2号询问 询问是否有一个点与u,v都相邻,若有则输出该点,若无则输出0。
询问强制在线。

思路:在题目要求的图中,满足2号询问的点只有三种情况:要么这个点是u,v的根节点;要么三者在一条链上,u是v的爷爷或者v是u的爷爷。
那么在这道题中,加边操作会影响原先的两个点的父子关系,那么在每次加边时都需要重新对图进行重构。
每次加边的时候相当于把两块部分相连,在这里可以用启发式合并的思想。我们用并查集维护每个点所在的联通块的大小,对于每次加边的两个节点所在的联通块,我们将小的那个联通块合并到大的那个。那么在每次查询的时候只需要根据父子关系得到答案即可。

#define maxn 200010
vector<int> vec[maxn];
int n,m;
int p[maxn],siz[maxn],fa[maxn];
int xx;
int find(int x)
{
    if(x!=p[x]) p[x]=find(p[x]);
    return p[x];
}
void merge(int x,int y)
{
    int a=find(x);
    int b=find(y);
    if(a!=b)
    {
        p[a]=b;
        siz[b]+=siz[a];
        siz[a]=0;
    }
}
void dfs(int u)
{
    for(auto v:vec[u])
    {
        if(v==fa[u]) continue;
        fa[v]=u;
        dfs(v);
    }
}
int query(int x,int y)
{
    if(fa[x]==fa[y]&&fa[x]!=0)
    {
        return fa[x];
    }
    if(fa[fa[x]]==y)
    {
        return fa[x];
    }
    if(fa[fa[y]]==x)
    {
        return fa[y];
    }
    return 0;
}
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        p[i]=i;
        siz[i]=1;
    }
    for(int i=1;i<=m;i++)
    {
        int op,u,v;
        cin>>op>>u>>v;
        op=1+(((op*(1+xx))%mod)%2);
        u=1+(((u*(1+xx))%mod)%n);
        v=1+(((v*(1+xx))%mod)%n);
        if(op==2)
        {
            xx=query(u,v);
            cout<<xx<<'\n';
            continue;
        }       
        else{
            if(siz[find(u)]<siz[find(v)])
            {
                swap(u,v);
            }
            vec[u].push_back(v);
            vec[v].push_back(u);
            fa[v]=u;
            merge(v,u);
            dfs(v);
        }
    }
}
posted @ 2024-04-21 22:24  Captainfly19  阅读(17)  评论(0编辑  收藏  举报