比赛链接:link
G STL中list的使用
题意是说对于一张图有
n
n
n 个点,
m
m
m 条边,一开始每个点自为一个
g
r
o
u
p
group
group,我们现在有
q
q
q 次操作,每次操作选定
g
r
o
u
p
group
group 的编号为
o
i
o_i
oi,若是
o
i
o_i
oi 组没有点,则不发生变化,否则与
o
i
o_i
oi 组的点相连的点若是属于其他组
o
j
o_j
oj,则
o
j
o_j
oj 组的点并入
o
i
o_i
oi,
o
j
o_j
oj 变空。全部操作之后输出每个点属于那一组。有多组输入,
n
,
m
,
q
≤
5
e
8
n, m, q ≤ 5e8
n,m,q≤5e8。
经过分析我们发现,若是
o
i
o_i
oi 组在一轮操作中被并掉,那么
o
i
o_i
oi 组就一直为空;若是一个点在一次操作中和另一个点处于同一组,那么之后它们永远属于同一组。于是自然想到用并查集记录每个
g
r
o
u
p
group
group 的元素,并记录与每个
g
r
o
u
p
group
group 相连的点。
一开始想到用
v
e
c
t
o
r
vector
vector 记录,但是这样的话一个
g
r
o
u
p
group
group 被并入另一个
g
r
o
u
p
group
group 时,它所记录的相连的点也要相应复制到另一个
g
r
o
u
p
group
group,这样就会进行很多次复制。查看别人的题解发现有链式前向星的做法,只要修改一下链表,而不用复制。但是前者复制反而会过,后者只是修改链表却超时…分析原因可能是链式前向星要初始化太多东西,多组输入就容易超时。
为此,可以采用
l
i
s
t
list
list 来记录,
l
i
s
t
list
list 有一个
s
p
l
i
c
e
splice
splice 方法,可以完成两个链表的合并,这种方式比链式前向星写的手动链表要快。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<double, double> P;
const int maxn = 8e5 + 10;
const int INF = 0x3f3f3f3f;
const double eps = 1e-11;
const ll mod = 1e9 + 7;
list<int> G[maxn];
int fa[maxn];
int Find(int x) { return fa[x] == x? x : fa[x] = Find(fa[x]); }
int main()
{
int t, n, m, q, x;
scanf("%d", &t);
while(t--)
{
scanf("%d %d", &n, &m);
for(int i = 0; i < n; i++)
{
fa[i] = i; //初始化并查集和链表
G[i].clear();
}
for(int i = 0; i < m; i++)
{
int x, y;
scanf("%d %d", &x, &y);
G[x].push_back(y); G[y].push_back(x);
}
scanf("%d", &q);
while(q--)
{
scanf("%d", &x);
if(fa[x] != x) continue; //若是该组已经没有点
int len = G[x].size(), y;
for(int i = 0; i < len; i++)
{
y = G[x].front(); G[x].pop_front();
y = Find(y); //查找与这个组相邻的点
if(y == x) continue; //若是同属于一个组
fa[y] = x;
G[x].splice(G[x].end(), G[y]); //修改链表
}
}
for(int i = 0; i < n - 1; i++)
printf("%d ", Find(i));
printf("%d\n", Find(n - 1));
}
}