s s ,很显然,原序列任意一个排列都能在不超过2次循环内求出答案。 构造方法很简单。众所周知,任意一个排列都可以表示成几个..." />
当前位置: 代码迷 >> 综合 >> 【构造】Codeforces1012E Cycle sort
  详细解决方案

【构造】Codeforces1012E Cycle sort

热度:113   发布时间:2023-09-27 07:00:54.0

题意:

给出一个序列,通过一些循环,将其排列成一个不下降的序列。
要求所有循环的长度不超过ss,并且使得满足条件的情况下,循环的个数尽量少。


分析:

如果不考虑限制条件 s ,很显然,原序列任意一个排列都能在不超过2次循环内求出答案。

构造方法很简单。众所周知,任意一个排列都可以表示成几个不相交的循环。

那么如果长度不为1的循环只有一个,那么只需要这个循环倒过来做一次,就能恢复成原序列。

如果长度不为1的循环有多个,设其分别为
(pi1pi2pi3piki)(pi+1 1pi+1 2pi+1 ki+1)(pm1pm2pm3pmkm)(pi1、pi2、pi3……piki)(pi+11、pi+12……pi+1ki+1)……(pm1、pm2、pm3、……pmkm)

我们可以将这些循环依次连接,即搞一个
(pi1pi2pi3pikipi+1 1pi+1 2pi+1 ki+1pm1pm2pm3pmkm)(pi1、pi2、pi3……piki、pi+11、pi+12……pi+1ki+1、……pm1、pm2、pm3、……pmkm)
这么一个大循环,观察一下发现,除了每个循环最后一个元素,其它的都已回到原位。所以我们把所有的最后一个元素再组成一个循环,循环回去就是了。

那么现在来考虑ss的限制

很显然,每个不再原位的位置,都要被移动至少一次(回到原来的位置)。观察上面的构造方法就能发现,其实这个方法比最小情况多了m而已。设所有不在原位的位置数量为k,如果 k > s ,则一定无解。
反之,如果ks?mk≤s?m,那么上述的构造方法就可以实现。
如果s?m<kss?m<k≤s,那么可以考虑增加一些单纯的循环(即将某个循环倒序)然后使得这个m减少。直至刚好凑到s为止。

现在再来考虑有相等的值的问题。
根据上面的思路,我们需要尽可能减少m,也就是说要尽可能减少循环的个数。

一个很简单的操作方法是,设原序列为aiai,将其排序后得到的序列为bibi,首先排除掉所有ai=biai=bi的位置(这些都是已经排好序了的),然后对所有aiai,向bibi连边。这时可以发现,所有aiai相等的位置的顺序任意。所以对不同循环中相等的值,可以通过交换它们以合并成一个循环。这样一来循环的个数就能减少。

所以贪心地想,就需要把所有能合并的循环合并,以得到一个最少的循环覆盖。

细节处理可以使用DSU

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
#define SF scanf
#define PF printf
#define MAXN 200010
using namespace std;
int a[MAXN],p[MAXN],n,s;
pair<int,int> b[MAXN];
int fa[MAXN],r[MAXN];
int get_fa(int x){if(fa[x]==0)return x;fa[x]=get_fa(fa[x]);return fa[x];   
}
void merge(int x,int y){int fx=get_fa(x);int fy=get_fa(y);if(fx!=fy)fa[fx]=fy;  
}
bool check(int x,int y){return get_fa(x)==get_fa(y);    
}
int now;
bool used[MAXN];
vector<int> ans[MAXN];
void dfs(int x){used[x]=1;ans[now].push_back(x);if(used[p[x]]==0)dfs(p[x]);
}
int main(){SF("%d%d",&n,&s);for(int i=1;i<=n;i++){SF("%d",&a[i]);b[i]=make_pair(a[i],i); }sort(b+1,b+1+n);for(int i=1;i<=n;i++)p[b[i].second]=i;for(int i=1;i<=n;i++)if(a[i]==b[i].first&&p[i]!=i){p[b[i].second]=p[i];b[p[i]].second=b[i].second;p[i]=i;b[i].second=i;}for(int i=1;i<=n;i++)if(p[i]!=i)merge(p[i],i);int las=0;for(int i=1;i<=n;i++){if(p[b[i].second]==b[i].second)continue;   if(las>=1&&a[las]==a[b[i].second]){int x=las;int y=b[i].second;if(check(x,y))continue;merge(x,y);swap(p[x],p[y]);    }las=b[i].second;}now=0;for(int i=1;i<=n;i++)if(used[i]==0&&p[i]!=i){now++;  dfs(i);}int sum=0;for(int i=1;i<=now;i++)sum+=ans[i].size();if(sum>s){PF("-1");return 0;   }s-=sum;s=min(s,now);if(s<=1){PF("%d\n",now);for(int i=1;i<=now;i++){PF("%d\n",ans[i].size());for(int j=0;j<ans[i].size();j++)PF("%d ",ans[i][j]);puts("");}return 0;}PF("%d\n",now-s+2);for(int i=1;i<=now-s;i++){PF("%d\n",ans[i+s].size());for(int j=0;j<ans[i+s].size();j++)PF("%d ",ans[i+s][j]);puts("");sum-=ans[i+s].size();}PF("%d\n",sum);for(int i=1;i<=s;i++)for(int j=0;j<ans[i].size();j++)PF("%d ",ans[i][j]);PF("\n%d\n",s);for(int i=s;i>0;i--)PF("%d ",ans[i][0]);
}
  相关解决方案