zoukankan      html  css  js  c++  java
  • codeforces#1166F. Vicky's Delivery (Service并查集+启发式合并)

    题目链接:

    https://codeforces.com/contest/1166/problem/F

    题意:

    给出节点数为$n$,边数为$m$的图,保证每个点对都是互连的

    定义彩虹路:这条路经过$k$个节点,对于$x(x\%2=0)$节点,的左右两条边颜色相同

    现在有$q$次操作

    第一种操作是添加一条边

    第二种操作是回答是否能经过彩虹边从$a$节点到达$b$节点

    数据范围:

    $2 le n le 10^5$
    $1 le m,q le 10^5$

    分析: 

    我们可以考虑用并查集把那些可以互相到达(经过边数为偶数的彩虹路)的点对用并查集连接起来

    当$a-b-c$的边的颜色相同时,我们把a和c节点用并查集连接起来,代表$a$和$c$互连

    对于节点a,每个颜色的边只需要保存一条,这样可以快速合并节点

    这样,两个点是否能到达,只需要判断他们的并查集根是不是相同

    但是还有一种情况,$x$节点到$y$节点经过的边数为奇数,这样最后一条边的颜色就不重要了

    我们可以给每个并查集添加一个集合,集合里面的点直接连接某个并查集的点

    连接两个并查集用到了启发式合并(整个合并操作复杂度为$O(nlgn)$)

    ac代码:

    #include<bits/stdc++.h>
    #define ll long long
    #define ull unsigned long long
    #define pa pair<int,int>
    using namespace std;
    const int maxn=1e5+10;
    const int maxm=5e6+10;
    const ll mod=1e9+7;
    map<int,int>ma[maxn];//对于节点a,每个颜色的边只需要保存一条
    set<int>se[maxn];
    int n,m,c,q,boss[maxn];
    void inti()
    {
        for(int i=1;i<=n;i++)
            boss[i]=i;
    }
    int fin(int x)
    {
        if(boss[x]==x)return x;
        return boss[x]=fin(boss[x]);
    }
    void uni(int a,int b)
    {
        int v1=fin(a),v2=fin(b);
        if(v1==v2)return;
        if(se[v1].size()>se[v2].size())swap(v1,v2);//启发式合并关键
        boss[v1]=v2;
        for(auto i :se[v1])se[v2].insert(i);
        se[v1].clear();
    }
    void add_edge()
    {
        int a,b,v;
        scanf("%d %d %d",&a,&b,&v);
        if(ma[a].count(v))
            uni(ma[a][v],b);
        else
            ma[a][v]=b;
        if(ma[b].count(v))
            uni(ma[b][v],a);
        else
            ma[b][v]=a;
        se[fin(a)].insert(b);
        se[fin(b)].insert(a);
    }
    int main()
    {
        scanf("%d %d %d %d",&n,&m,&c,&q);
        inti();
        for(int i=1; i<=m; i++)
            add_edge();
        while(q--)
        {
            getchar();
            if(getchar()=='?')
            {
                int fla=0,a,b;
                scanf("%d %d",&a,&b);
                int v=fin(a);
                if(v==fin(b)||se[v].find(b)!=se[v].end())//同一个集合,或者不同集合,但是再走一条边能到
                     printf("Yes
    ");
                else
                     printf("No
    ");
            }
            else
            {
                add_edge();
            }
        }
        return 0;
    }
    

      

  • 相关阅读:
    ActiveMQ的两种消息模式,主题、队列
    微信5.0打飞机怎么取得高分?
    微信公众平台消息接口星标功能
    WordPress的SEO技术
    2013中国微信公众平台用户研究报告
    jQuery Mobile入门教程
    微信公众平台商户模块
    使用PHP绘制统计图
    Google Chart API 参考 中文版
    使用Google Chart API绘制组合图
  • 原文地址:https://www.cnblogs.com/carcar/p/10902142.html
Copyright © 2011-2022 走看看