置换 ---- 两个置换最少swap次数 E. Permutation Shift

题目链接


题目大意:

就是将一个长度为 n n n [ 1 , 2 , 3 , 4 , 5 , 6 , . . . , n ] [1,2,3,4,5,6,...,n] [1,2,3,4,5,6,...,n]的数组先向右旋转 K K K步,然后在 s w a p swap swap最多 m m m次,现在给你最后的结果,问你 K K K可能是什么?


解题思路:

又是一个小技巧
1.首先我们知道交换后再旋转,和旋转后再交换是一样的
2.对于两个全排列 a , b a,b a,b a − > b a->b a>b的最小交换次数等于 ( a i , b i ) (a_i,b_i) (ai,bi)连接无向边然后 n − c n-c nc然后 c c c是图的联通块个数
3.但是每次检测是 O ( n ) O(n) O(n)的我们不能对每个 K K K都检测一遍
5.我们发现对于交换后,最多只会有 2 ∗ m 2*m 2m个数不在原来的位置,那么就有 n − 2 ∗ m n-2*m n2m会在原来的位置

在这里插入图片描述


#include <bits/stdc++.h>
#define mid ((l + r) >> 1)
#define Lson rt << 1, l , mid
#define Rson rt << 1|1, mid + 1, r
#define ms(a,al) memset(a,al,sizeof(a))
#define log2(a) log(a)/log(2)
#define lowbit(x) ((-x) & x)
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define INF 0x3f3f3f3f
#define LLF 0x3f3f3f3f3f3f3f3f
#define f first
#define s second
#define endl '\n'
using namespace std;
const int N = 2e6 + 10, mod = 1e9 + 9;
const int maxn = 500010;
const long double eps = 1e-5;
const int EPS = 500 * 500;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PII;
typedef pair<ll,ll> PLL;
typedef pair<double,double> PDD;
template<typename T> void read(T &x)
{
   x = 0;char ch = getchar();ll f = 1;
   while(!isdigit(ch)){if(ch == '-')f*=-1;ch=getchar();}
   while(isdigit(ch)){x = x*10+ch-48;ch=getchar();}x*=f;
}
template<typename T, typename... Args> void read(T &first, Args& ... args) 
{
   read(first);
   read(args...);
}

int n, m;
int arr[maxn], cnt[maxn];
vector<int> ans, Graph[maxn];
bool vis[maxn];

void dfs(int u) {
   vis[u] = 1;
   for(auto it : Graph[u]) {
      if(vis[it]) continue;
      dfs(it);
   }
}

inline bool check(int loop) {//先转再去check要多少次交换
   for(int i = 0; i < n; ++ i) Graph[i].clear(), vis[i] = 0;
   for(int i = 0; i < n; ++ i)
     Graph[arr[i]-1].push_back((i-loop+n)%n), Graph[(i-loop+n)%n].push_back(arr[i]-1);
   int tg = 0;
   for(int i = 0; i < n; ++ i)
      if(!vis[i])
        dfs(i),tg ++;
   return n - tg <= m;
}

inline void slove() {
   ans.clear();
   cin >> n >> m;
   for(int i = 0; i < n; ++ i) cnt[i] = 0;
   for(int i = 0; i < n; ++ i) cin >> arr[i];
   for(int i = 0; i < n; ++ i) {
      int offset = i - (arr[i] - 1);
      if(offset < 0) offset += n;
      cnt[offset] ++;
   }
   for(int i = 0; i < n; ++ i)
     if(cnt[i] + 2 * m >= n && check(i)) 
       ans.push_back(i);
   cout << ans.size() << " ";
   for(auto it : ans) cout << it << " ";
   cout << "\n"; 
}

int main() {
    IOS;
    int T;
    cin >> T;
    while(T -- ) {
       slove();
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值