题目链接

题面:

题意:
给了一张图,一开始每个点在自己的集合中,如果存在一条边,其两个端点在不同集合中,我们认为这两个集合相邻。
每次选取一个点,若该点在自己的集合中,将该集合所有相邻的集合中间的点并入该集合中
输出q此操作后每个点所在的集合。

化简题意:
给一个 n 个点的 Graph,第 i 个点一刚开始是第 I 种颜色,接着有 k 次操作,第 i 次操作有个参数 oi 代表颜色 oi 会侵略所有和自己相邻的颜色,于是所有和 oi 相邻的颜色全都变成 oi (若已没有颜色oi 已被侵略,则该次操作无效),求最终每个点的颜色。

题解:
我们用并查集维护每个点所属的集合。
用链表维护每个集合有可能往外扩展的点,合并时,遍历这些有可能往外扩展的点,看看哪一些可以扩展,同时维护链表。链表的splice可以O(1)合并两个链表。

代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<string>
#include<queue>
#include<bitset>
#include<map>
#include<unordered_map>
#include<set>
#include<list>
#define ui unsigned int
#define ll long long
#define llu unsigned ll
#define ld long double
#define pr make_pair
#define pb push_back
#define lc (cnt<<1)
#define rc (cnt<<1|1)
//#define len(x) (t[(x)].r-t[(x)].l+1)
#define tmid ((l+r)>>1)
using namespace std;

const int inf=0x3f3f3f3f;
const ll lnf=0x3f3f3f3f3f3f3f3f;
const double dnf=1e18;
const int mod=998244353;
const double eps=1e-1;
const double pi=acos(-1.0);
const int hp=13331;
const int maxn=800100;
const int maxp=1100;
const int maxm=500100;
const int up=100000;

int head[maxn],ver[maxn<<1],nt[maxn<<1],tot=1;
int f[maxn];
list<int>li[maxn];
vector<int>vc;

void add(int x,int y)
{
    ver[++tot]=y,nt[tot]=head[x],head[x]=tot;
}

int fi(int x)
{
    if(f[x]!=x)
        f[x]=fi(f[x]);
    return f[x];
}

int main(void)
{
    int tt;
    scanf("%d",&tt);
    while(tt--)
    {
        int n,m,x,y;
        scanf("%d%d",&n,&m);

        tot=1;
        for(int i=1;i<=n;i++)
        {
            head[i]=0;
            li[i].clear();
            li[i].pb(i);
            f[i]=i;
        }
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&x,&y);
            x++,y++;
            add(x,y);
            add(y,x);
        }
        int q;
        scanf("%d",&q);
        for(int i=1;i<=q;i++)
        {
            scanf("%d",&x);
            x++;
            if(x!=fi(x)) continue;
            vc.clear();
            for(auto it=li[x].begin();it!=li[x].end();it++)
            {
                for(int j=head[*it];j;j=nt[j])
                {
                    int y=fi(ver[j]);
                    if(y==x) continue;
                    f[y]=x;
                    vc.pb(y);
                }
            }
            li[x].clear();
            for(int j=0;j<vc.size();j++)
            {
                if(!li[vc[j]].empty())
                    li[x].splice(li[x].end(),li[vc[j]]);
            }
        }
        for(int i=1;i<=n;i++)
            printf("%d ",fi(i)-1);
        putchar('\n');

    }
    return 0;
}