原题链接:G-Operating on a Graph_2023牛客五一集训派对day3 (nowcoder.com)
题目理解:给你n个点,序号是0~n-1。然后给m个边,对应是两个点相互连接。最后给出q个点,如果该点没有变成“其他点的附属”,就将该点所有相邻的点都变成该点的“附属”。如果该点变成的“其他点的附属”,那就忽略这次操作。
整体思路:设置一个vector数组。其下角标就是对应某个序号的点。每个点对应的vector都存储各自相邻的边。然后题目给出q个点的时候,每次给出就将其看作该点所属的“阵营”——即该点及其附属,向该“阵营”的所有相邻点的一次“侵略”,然后将该所有相邻点设为给出的点的“附属”。
需要掌握的知识点1-并查集(用于我们维护哪个点是哪个点的“附属”),代码如下
int p[800000+5]; //对应各个点的序号
//并查集
int find(int x)
{
if(x!=p[x])p[x]=find(p[x]); //这样大家都同时指向根部,在找的过程中就能修正,避免运行超时!
return p[x];
}
首先我们输入要操作的点,如果被侵略了就不做操作
cin>>x; //第几个点
if(x!=p[x])continue; //已经被侵略就不做操作
如果没有被侵略就先使用vector数组b将当前该点x的所有相邻点提取出来,并将vector a[x]的内容清空以防止后期的元素重复。
b=a[x];a[x].clear(); //将被侵略点相邻的边都取出来,然后把当前相邻的点清空
然后接下来就是找x的所有相邻点,在并查集中设置对应的“附庸关系”,并在a[x]数组中添加上其所有的相邻点。
for(i=0;i<b.size();i++)
{
y=find(b[i]); //相邻点所属于的颜色
if(x!=y) //找到要侵略的点
{
p[y]=x; //将要侵略的点侵略
//找要被侵略的点包含的所有相邻点
if(a[x].size()<a[y].size())swap(a[x],a[y]);
for(j=0;j<a[y].size();j++)
{
a[x].push_back(a[y][j]);
}
}
}
最终就是按照格式输出答案
最终的AC代码:
#include <iostream>
#include <vector>
#include<string>
#include<sstream>
#include<math.h>
#include<map>
#include<set>
#include<cstring>
#include<algorithm>
using namespace std;
vector<int>a[800000+5],b;//存储 “被侵略的点链接的边”
int p[800000+5];
//并查集
int find(int x)
{
if(x!=p[x])p[x]=find(p[x]); //这样大家都同时指向根部,在找的过程中就能修正远点
return p[x];
}
void solveg()
{
int t;
cin>>t;
while(t--)
{
int n,m,i,j,x,y,q;
cin>>n>>m;
for(i=0;i<=n;i++) //初始化
{
p[i]=i;
a[i].clear();
}
for(i=1;i<=m;i++)
{
cin>>x>>y;
//两者互相是对方的邻接点
a[x].push_back(y);
a[y].push_back(x);
}
cin>>q;
while(q--)
{
cin>>x; //第几个点
if(x!=p[x])continue; //已经被侵略就不做操作
//b存储当前x的所有相邻点,然后把x当前存储的相邻点清空
// 接下来到了被侵略的点的处理
b=a[x];a[x].clear(); //将被侵略点相邻的边都取出来,然后把当前相邻的点清空
//遍历当前相邻的点
for(i=0;i<b.size();i++)
{
y=find(b[i]); //相邻点所属于的颜色
if(x!=y) //找到要侵略的点
{
p[y]=x; //将要侵略的点侵略
//找要被侵略的点包含的所有相邻点
if(a[x].size()<a[y].size())swap(a[x],a[y]);
for(j=0;j<a[y].size();j++)
{
a[x].push_back(a[y][j]);
}
}
}
}
for(i=0;i<n;i++)
{
cout<<find(i)<<" ";
}
cout<<endl;
}
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
solveg();
}