Codeforces 1166 F. Vicky's Delivery Service 并查集+set

题意:有n个点,m条边,边有c种颜色,q次操作。

每个边都有一种颜色。

然后操作有两种,一种是再加一条边,另一种是查询能否从x达到y。

移动的限制是,连着走两步必须是同一种颜色,如果走奇数步,最后一步可以是任意颜色。

例子:1-2-3-4-5-6。

 

这个题颜色种类很多,我是用map<int,vector<int>>来存边。

我们首先可以想到 对于点x同种颜色连着的点都是可以相互移动的,所以我们可以用这种方法将它们用并查集合并,我们也可以直接map<int,int>来存边,因为一个vector里的点是等价的。

但是这样有一个问题,走偶数步可以走通的已经被我们合并可以直接查询了,那么最后一步能走到的怎么办呢?

我们可以对每个联通集合存一个set,里面放集合中能一步走到的点。我们之后要合并集合,这个set自然也要合并,本来是想着用可并堆,但是其实用启发式合并也可以了。

以下为代码:

#include<bits/stdc++.h>
using namespace std;
int i,i0,n,m,c,q,pre[100005];
map<int,vector<int>>mp[100005];
set<int>dic[100005];
int fin(int x){return (pre[x]==x)?x:pre[x]=fin(pre[x]);}
void uni(int x,int y)
{
    if(fin(x)!=fin(y))
    {
        if(dic[fin(x)].size()<dic[fin(y)].size())swap(x,y);
        for(auto i:dic[fin(y)])dic[fin(x)].insert(i),pre[fin(y)]=fin(x);
    }
}
void add()
{
    int x,y,col;
    scanf("%d %d %d",&x,&y,&col);
    dic[fin(x)].insert(y),dic[fin(y)].insert(x);
    mp[x][col].push_back(y),uni(y,mp[x][col][0]);
    mp[y][col].push_back(x),uni(x,mp[y][col][0]);
}
int main()
{
    scanf("%d %d %d %d",&n,&m,&c,&q);
    for(i=1;i<=n;i++)pre[i]=i;
    while(m--)add();
    while(q--)
    {
        char op;
        scanf(" %c",&op);
        if(op=='?')
        {
            int x,y;
            scanf("%d %d",&x,&y);
            printf("%s\n",(fin(x)==fin(y)||dic[fin(x)].count(y))?"Yes":"No");
        }
        else add();
    }
    return 0;
}

 

posted @ 2019-05-20 22:05  BiteTheDDDDt  阅读(408)  评论(0编辑  收藏  举报