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\) ,开个桶统计个下标就切了。
并查集感觉可以绿,但这直接暴力修改就只有橙了啊。
我是傻逼。