Codeforces1553E Permutation Shift (思维)

题目链接: Permutation Shift

大致题意

你有初始长度为 n n n的序列 a = { 1 , 2 , 3 , . . . , n } a = \{1, 2, 3, ..., n\} a={1,2,3,...,n}.
你可以将整个序列右移 k ( k ∈ [ 0 , n − 1 ] ) k(k \in [0, n - 1]) k(k[0,n1])位, 超出的部分会移动到序列开头. 随后有至多 m ( m ∈ [ 0 , n 3 ] ) m(m \in [0, \frac{n}{3}]) m(m[0,3n])次交换操作, 可以选择序列中任意两个元素进行交换.

现给出序列 b b b, 问有多少个 k k k可以让序列 a a a变换为序列 b b b, 并且输出所有合法的 k k k.

解题思路

思维

首先考虑到, 我们可以暴力计算出序列 a a a右移 k k k位的序列情况, 然后我们与序列 b b b去进行匹配, 对于 a i = b i a_i = b_i ai=bi的位置无需交换, 否则我们需要进行一个找环操作, 需要对每个独立环内元素进行环大小-1次交换.
整体复杂度: O(n2).


考虑到优化, 由于 m m m的上限取值为 n 3 \frac n3 3n, 因此我们至多可以将 2 m 2m 2m个元素交换到正确的位置上.

因为每个环需要进行环大小-1次交换. 当每个环大小都是2时, 达到最优情况.

这说明, 此时至少需要 n − 2 m n - 2m n2m个元素已经在正确位置上, 因此最理想情况也需要有 n 3 \frac n3 3n个元素在正确位置上. 而对于右移后, 对于每个位置 a i = b i a_i = b_i ai=bi的情况, 仅会存在一个 k k k中. 因此至多会有3个k满足题意.

因此我们先可以预处理出 c o u [ ] cou[] cou[], 存储当 a a a序列右移 k k k后, 能匹配的位置数量, 然后对于每个 c o u [ k ] + 2 m ≥ n cou[k] + 2m \ge n cou[k]+2mn的情况, 我们进行暴力判断即可.


注: 代码中序列 a a a, b b b和解题思路中相反.

AC代码

#include <bits/stdc++.h>
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
const int N = 3E5 + 10;
bool vis[N];
int a[N], b[N], cou[N], mp[N];
int main()
{
    int t; cin >> t;
    while (t--) {
    	int n, m; scanf("%d %d", &n, &m);
    	rep(i, n) cou[i - 1] = 0;
    	rep(i, n) scanf("%d", &a[i]), cou[(i - a[i] + n) % n]++, mp[a[i]] = i;

    	vector<int> res;
    	for (int k = 0; k < n; ++k) {
    		if (cou[k] + 2 * m < n) continue;

    		rep(i, n) b[i] = (i - k + n) % n ? (i - k + n) % n : n, vis[i] = 0;

    		int need = 0;
    		rep(i, n) {
    			if (a[i] == b[i] or vis[i]) continue;
    			int cur = i, sz = 0;
    			while (!vis[cur]) {
    				vis[cur] = 1, sz++;
    				cur = mp[b[cur]];
    			}
    			need += sz - 1;
    		}
    		if (need <= m) res.push_back(k);
    	}
    	printf("%u ", res.size());
    	for (auto& op : res) printf("%d ", op); puts("");
    }

    return 0;
}

END

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

逍遥Fau

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值