Leetcode 765 情侣牵手 / Hetao-013 翅膀打结 题解 [ 黄 ] [ 并查集 ] [ BFS ] [ 贪心 ]

经典的连通块题,幸好我之前在 leetcode 看过原。

转化

首先观察到一对cp无论是男在前,还是女在前,都视为配对成功,对答案无影响。

因此,我们可以把一对情侣赋同一个编号,直接加一除以二即可。

同时,由于两个情侣要坐一起,所以最终形态一定是这样的

\(a,a,b,b,c,c,d,d,...,z,z\)

例如:

in:
3
3 5 4 6 2 1

out:
1

进行前面一步转化后,可以得到情侣的编号:

2 3 2 3 1 1

分析

接下来看如何将编号相同的两个人搞到一起去。

由上面分析得到的最终形态可知,我们要把这个序列分成 \(n\) 组,每一组为 \({{2(k-1),2(k-1)+1}}\)

然后对于每一组进行分析:

例如下面的例子:

(1,3) (3,1) 
(1,3) (2,1) (3,2)
(1,3) (2,1) (3,2) (4,4)

可以发现,如果每一组内不是同一对情侣,那么就要和本组内另一个人的cp所在组里交换一遍,然后接下来再对后面的组一直换,直到形成了一个变换环。这个变换环的最终形态就像是上面的第一组例子,最后换一下就好了。

那么这个变换环最少操作次数怎么求?
可以参考冒泡排序的思考方式,每次选择一个放到序列最后面,只需 \(n-1\) 次操作就可以放完。
那么这个也可以类比一下,一个变换环最少操作次数便是环上节点数减一

于是最后统计一下每个环的数量累加就好了。

统计变换环就是将同组的两个人用并查集合并一下,如果本来就是一对cp,则相当于没合并,如果是不同编号的cp,那么就是代表这两个匹配错了,要跟另一组情侣换回来。这是把一对情侣看作一个 dsu 中的节点的方式。
其实把分的每一组看作一个节点似乎也是没有问题的。

同样可以用 BFS 的方式来实现,只是感觉 dsu 更好写些。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int f[200005],n,num[200005],sz[200005];
ll ans=0;
bitset<200005>vis;
void init()
{
	for(int i=1;i<=n;i++)
	{
		f[i]=i;
		sz[i]=1;
	}
}
int findf(int x)
{
	if(f[x]!=x)f[x]=findf(f[x]);
	return f[x];
}
void combine(int x,int y)
{
	int fx=findf(x),fy=findf(y);
	if(fx!=fy)
	{
		f[fx]=fy;
		sz[fy]+=sz[fx];
	}
}
int main()
{
	freopen("twist.in","r",stdin);
	freopen("twist.out","w",stdout);
    cin>>n;
    init();
    for(int i=1;i<=2*n;i++)
    {
		cin>>num[i];
		num[i]=(num[i]+1)/2;
	}
	for(int i=1;i<=2*n;i+=2)
	{
		combine(num[i],num[i+1]);
	}
	for(int i=1;i<=n;i++)
	{
		int fa=findf(i);
		ans+=(!vis[fa])*(sz[fa]-1);
		vis[fa]=1;
	}
	cout<<ans;
    return 0;
}

后寄

赛后突然发现好像可以直接暴力做,找到不配对的情侣就交换就行了,反正操作数最多 \(2*10^5\) ,开个桶统计个下标就切了。

并查集感觉可以绿,但这直接暴力修改就只有橙了啊。

我是傻逼。

posted @ 2024-06-29 18:16  KS_Fszha  阅读(14)  评论(0编辑  收藏  举报