CF1012E Cycle sort 题解
题目链接
题目解法
令 \(b\) 为 \(a\) 排序之后的序列
首先无解就是 \(a_i\neq b_i\) 的位置 \(>k\) 个
这种问题的简化版是排列
我们先考虑排列的情况
轮换问题可以想到建出置换环(\(i\to a_i\))
我们有两种方式构造轮换序列:
- 对于每个非自环,按顺序输出
操作数为:非自环的环数,\(\sum k\) 为:非自环的点数 - 整体按照顺序输出每个环,发现这样操作之后会形成一个置换环,再对这个环操作一次
举个例子(置换环自己画出):
\(a: 2,3,4,1,6,7,5,9,8\),
整体操作一次之后的序列:\(8,2,3,4,1,6,7,5,9\)
这个序列形成的置换环是 \(1,8,5\),操作一次即可
操作数为:\(2\),\(\sum k\) 为:非自环的点数 + 环数
因为 \(k\) 有限制,所以我们尽量多的采用构造 \(2\),其余的用构造 \(1\) 输出
再考虑原问题
我们先随便连出一堆置换环,观察到有 \(a\) 相同的点的置换环可以合并
尽可能多合并即可
最后按照上面排列的方式做就好了
时间复杂度 \(O(n\log n)\)
#include <bits/stdc++.h>
#define F(i,x,y) for(int i=(x);i<=(y);i++)
#define DF(i,x,y) for(int i=(x);i>=(y);i--)
#define ms(x,y) memset(x,y,sizeof(x))
#define SZ(x) (int)x.size()-1
#define all(x) x.begin(),x.end()
#define pb push_back
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
typedef pair<int,int> pii;
template<typename T> void chkmax(T &x,T y){ x=max(x,y);}
template<typename T> void chkmin(T &x,T y){ x=min(x,y);}
template<typename T> void read(T &FF){
FF=0;int RR=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;
for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;
FF*=RR;
}
const int N=200010;
int n,k,a[N],b[N],c[N];
vector<int> v1[N],v2[N];
map<int,int> mp;
int fa[N];bool vis[N];
int gfa(int x){ return x==fa[x]?x:fa[x]=gfa(fa[x]);}
void merge(int x,int y){ fa[gfa(x)]=gfa(y);}
bool prt[N];
vector<int> ans;
void dfs(int u){
prt[u]=1,ans.pb(u);
if(!prt[c[u]]) dfs(c[u]);
}
int ver[N];
int main(){
read(n),read(k);
F(i,1,n) read(a[i]),b[i]=a[i];
sort(b+1,b+n+1);
int cnt=0;
F(i,1,n) if(b[i]!=b[i-1]) mp[b[i]]=++cnt;
F(i,1,n) a[i]=mp[a[i]],b[i]=mp[b[i]];
int res=0;
F(i,1,n) if(a[i]!=b[i]){
res++;
v1[b[i]].pb(i),v2[a[i]].pb(i);
}
if(res>k){ puts("-1");exit(0);}
F(i,1,n) fa[i]=i;
F(i,1,n) if(a[i]!=b[i]) c[i]=v1[a[i]].back(),v1[a[i]].pop_back();
F(i,1,n) merge(i,c[i]);
F(i,1,cnt)
F(j,1,SZ(v2[i])){
int a=v2[i][j-1],b=v2[i][j];
int x=gfa(a),y=gfa(b);
if(x!=y) swap(c[a],c[b]),fa[x]=y;
}
F(i,1,n) if(a[i]!=b[i]) ver[gfa(i)]=i;
int cir=0;
F(i,1,n) if(ver[i]) cir++;
int jt=min(k-res,cir);
if(jt<=2) printf("%d\n",cir),jt=0;
else printf("%d\n",2+cir-jt);
int i=1;
if(jt){
int rs=0;
vector<int> ans1,ans2;
for(;i<=n;i++) if(ver[i]){
rs++;
if(rs>jt) break;
int cur=ver[i];
while(true){
ans1.pb(cur),cur=c[cur];
if(cur==ver[i]) break;
}
ans2.pb(ver[i]);
}
reverse(ans2.begin(),ans2.end());
printf("%d\n",(int)ans1.size());
for(int x:ans1) printf("%d ",x);puts("");
printf("%d\n",(int)ans2.size());
for(int x:ans2) printf("%d ",x);puts("");
}
for(;i<=n;i++) if(ver[i]){
vector<int> ans;
int cur=ver[i];
while(true){
ans.pb(cur),cur=c[cur];
if(cur==ver[i]) break;
}
printf("%d\n",(int)ans.size());
for(int x:ans) printf("%d ",x);puts("");
}
return 0;
}

浙公网安备 33010602011771号