置换环学习笔记
本文会使用排列 A 套排列 B 来表示如下操作:
C[i]=B[A[i]]。
排列有 3 种表示方法,置换环、二分图、平面直角坐标系。
例如我们用这三种方法表示排列 1 7 6 5 8 9 2 4 3 10:
置换环:出现排列套排列、交换等时可以考虑将 \(i\) 向 \(a_i\) 连单向边以方便理解。
这显然会连成若干个环(可能有自环),我们成这样建出的图为该排列的置换环。

二分图:出现两个元素之间的 \(a\) 值大小比较时可以考虑对左部的 \(i\) 向右部的 \(a_i\) 连边。

平面直角坐标系:出现形如统计 \(i\) 和 \(a_i\) 都需处于一定范围可以转换成矩阵思考。

当然这种题的大多数情况是按直觉先猜一种,搞不出来就全试试🤔。
教练只给了置换环的例题😅。
[poj2369] Permutations
题目大意
给定一个排列,求它套初始的自己多少次后可以变成形如 1 2 3...n 的样子。
\(n\le 1000\)。
题解
发现每套一次就相当于让置换环上所有数沿环走一步,问让所有数走回原位的最小次数。
一眼丁真发现对于大小为 \(a\) 的置换环,需要保证套的次数是 \(a\) 的倍数,于是所有置换环大小的 \(\text{lcm}\) 就是答案。
for(int i=1;i<=n;i++){
int x=i,cnt=0;
while(!vis[x]){
vis[x]=1;
x=p[x];
cnt++;
}
if(cnt) ans=ans*cnt/__gcd(ans,cnt);
}
cout<<ans<<"\n";
[USACO07FEB] Cow Sorting G
题目大意
给定一个不重的序列 \(a\),交换其中 \(i\) 和 \(j\) 的代价是 \(a_i+a_j\),求将 \(a\) 从小到大排序的最小代价。
\(n,a_i\le 10^5\)。
题解
先离散化让 \(a\) 变成排列,对于排列中交换两个数,有一个性质:如果这两个数在同一置换环中,则交换后该置换环分裂为两个,否则两个数所属的置换环合并,手模下不难证明。
我们最后期望的肯定是裂成 \(n\) 个置换环,发现分裂肯定是要找环内最小的那两个裂,但如果你直接这么写会喜提 20pts,原因如图所示:

在这个例子中,我们可以让 1 加入置换环后利用它把环拆开后再将 1 分裂出来以达到更优解。
所以我们在求解时要对这种方案取 \(\min\),两种方案的最小代价应该是不难维护的。
//Min1 是环内最小值,Min2 是环外最小值,sm 是环内权值和
ans+=min(sm[i]+(siz[i]-2)*Mn1,(siz[i]+1)*Mn2+sm[i]+Mn1);
[HNOI2010] 物品调度
这个题的输入很傻逼,为了避免喧宾夺主接下来只讲排列部分的做法。
题目大意
给定一个 \([0,n]\) 的排列,每次可以让 \(0\) 和其他元素交换位置,求让排列变为指定排列的最小操作次数。
\(n\le 10^5\)。
题解
首先经典的拿给定排列和目标排列套一下,问题转换为把一个排列操作成 \(0,1,2..n\) 的最小次数,即让置换环只有自环的最小代价。
仍然是分裂和合并的结论,特判掉 0 所在的置换环和已经是自环的置换环,答案为 \(\sum siz+1\)。
for(int i=0;i<n;i++){
if(i==find(i)){
if(siz[i]==1) continue;
if(find(0)==i){
ans+=siz[i]-1;
continue;
}
ans+=siz[i]+1;
}
}
[Poj3128] Leonardo's Notebook
题目大意
判断一个排列是否用 \(1,2,3...n\) 套某个排列两次得到。
\(n\le 500\)。
题解
发现某个排列两次相当于在置换环上动两步,手膜发现新的置换环有如下性质:
- 原来大小为偶数 \(n\) 的置换环将被分裂成两个大小为 \(\frac{n}{2}\) 的置换环。
- 原来大小为奇数的置换环不变。
所以判断一个排列能否用 \(1,2,3...n\) 套某个排列两次得到就是判断其中所有偶数大小的置换环能否都找到和自己一样大小的置换环拼起来。
int sum=0;
for(int i=1;i<=26;i++){
if(find(i)==i&&siz[i]%2==0){
sum-=vis[siz[i]];
vis[siz[i]]^=1;
sum+=vis[siz[i]];
}
}
cout<<(sum?"No\n":"Yes\n");

浙公网安备 33010602011771号