CF1553 E. Permutation Shift
题意:
有一个初始排列1 2 3……n,再给出一个目标排列和一个m(m<=n/3),要求把初始排列变为目标排列
首先,你可以将初始排列的最后k个数字挪到最前面,然后可以选2个数字进行交换,交换至多进行m次
问有多少个k满足能够使初始排列变为目标排列
首先解决一个经典问题
对于一个排列A和一个排列B,每次可以选2个数字交换,问最少交换多少次可以使A变成B
我们在所有的A[i]和B[i]之间连边,这样会得到若干个环
自环不需要交换
一个大小为L的环需要L-1次交换
所以最少交换次数等于 n-环的个数
所以如果确定了k,那么可以用O(n)的时间检验出能否至多交换m次变为目标排列
但是k不能枚举,这样复杂度就是O(n^2)了
这时就要看m<=n/3的限制了
一次交换至多使2个数到目标位置,至多交换m次,所以至多有2m个数可以不在目标位置
即至少要有n-2m个数在目标位置
因为m<=n/3,所以n-2m>=n/3
即一个排列只有至少有n/3个数在正确位置,才有可能在m次交换之内变为目标排列
而1个数只有一个唯一的k能够使它在目标位置
而一个可能的k又需要有至少n/3个数在它的目标位置
所以至多只有3个k有可能满足条件
所以我们只需要检验3个k就可以
怎么确定这3个k是谁
把每个数都减1,这样排列变为[0—n-1],因为循环移位取模用0比较方便
位置下标从0开始,初始排列第i个数在位置i,当选定k之后,位置i的数会变为(i-k+n)%n
设这个位置的目标数位x,那么就是(i-k+n)%n=x
所以k=(i-x+n)%n
对于每个位置都算出一个k,取最多的3个k即可
#include<bits/stdc++.h> #define N 300003 int n,m,a[N],sk[N]; int fa[N]; bool vis[N]; int find(int i) { return fa[i]==i ? i : fa[i]=find(fa[i]); } void unionn(int i,int j) { fa[i]=find(i); fa[j]=find(j); if(fa[i]!=fa[j]) fa[fa[i]]=j; } bool check(int k) { for(int i=0;i<n;++i) fa[i]=i; for(int i=0;i<n;++i) unionn(a[i],(i-k+n)%n); for(int i=0;i<n;++i) { vis[i]=false; fa[i]=find(i); } int h=0; for(int i=0;i<n;++i) if(!vis[fa[i]]) { vis[fa[i]]=true; ++h; } return n-h<=m; } int main() { int T; int sum,ans[4]; scanf("%d",&T); while(T--) { scanf("%d%d",&n,&m); for(int i=0;i<n;++i) { scanf("%d",&a[i]); a[i]--; } for(int i=0;i<n;++i) sk[i]=0; for(int i=0;i<n;++i) sk[(i-a[i]+n)%n]++; sum=0; for(int i=0;i<n;++i) if(sk[i]>=n/3) if(check(i)) ans[++sum]=i; printf("%d ",sum); for(int i=1;i<=sum;++i) printf("%d ",ans[i]); printf("\n"); } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架
2020-10-03 洛谷P2124 奶牛美容