题意:
给出一个序列,通过一些循环,将其排列成一个不下降的序列。
要求所有循环的长度不超过ss,并且使得满足条件的情况下,循环的个数尽量少。
分析:
如果不考虑限制条件 ,很显然,原序列任意一个排列都能在不超过2次循环内求出答案。
构造方法很简单。众所周知,任意一个排列都可以表示成几个不相交的循环。
那么如果长度不为1的循环只有一个,那么只需要这个循环倒过来做一次,就能恢复成原序列。
如果长度不为1的循环有多个,设其分别为
(pi1、pi2、pi3……piki)(pi+1 1、pi+1 2……pi+1 ki+1)……(pm1、pm2、pm3、……pmkm)(pi1、pi2、pi3……piki)(pi+11、pi+12……pi+1ki+1)……(pm1、pm2、pm3、……pmkm)
我们可以将这些循环依次连接,即搞一个
(pi1、pi2、pi3……piki、pi+1 1、pi+1 2……pi+1 ki+1、……pm1、pm2、pm3、……pmkm)(pi1、pi2、pi3……piki、pi+11、pi+12……pi+1ki+1、……pm1、pm2、pm3、……pmkm)
这么一个大循环,观察一下发现,除了每个循环最后一个元素,其它的都已回到原位。所以我们把所有的最后一个元素再组成一个循环,循环回去就是了。
那么现在来考虑ss的限制
很显然,每个不再原位的位置,都要被移动至少一次(回到原来的位置)。观察上面的构造方法就能发现,其实这个方法比最小情况多了m而已。设所有不在原位的位置数量为k,如果
,则一定无解。
反之,如果k≤s?mk≤s?m,那么上述的构造方法就可以实现。
如果s?m<k≤ss?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]);
}