[Educational Codeforces Round 4] - E Square Root of Permutation

洛谷传送门
Codeforces传送门

题目大意

给定一个 n n n的排列 p i p_i pi, 求一个排列 q i q_i qi, 满足对于 ∀ i ∈ [ 1 , n ] \forall i\in[1,n] i[1,n] q q i = p i ​ q_{q_i}=p_i​ qqi=pi

解题分析

神奇的规律题…

先尝试着正着推, 从 i i i p i p_i pi连一条边, 会构成很多的环。

然后我们发现, 如果设 k i = p p i k_i=p_{p_i} ki=ppi k i k_i ki再这样连也会构成很多环, 并且有如下关系:

  • 如果原来 i i i在一个偶环中, 那么这个偶环会被间隔着分成两个大小相同的环。
  • 如果原来 i i i在一个奇环中, 那么环中元素顺序会变成间隔一个位置的元素相邻。

于是找到 p i p_i pi中所有的环, 把大小相同的偶环随便合在一起, 奇环还原一下, 如果最后有单独的偶环则无解。

代码如下:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <vector>
#include <algorithm>
#define R register
#define IN inline
#define W while
#define MX 1005000
#define gc getchar()
#define ll long long
template <class T>
IN void in(T &x)
{
	x = 0; R char c = gc;
	for (; !isdigit(c); c = gc);
	for (;  isdigit(c); c = gc)
	x = (x << 1) + (x << 3) + c - 48;
}
struct INFO {int siz; std::vector <int> ele;} dat[MX];
IN bool operator < (const INFO &x, const INFO &y)
{return x.siz < y.siz;}
std::vector <int> vec;
int n, tot;
bool vis[MX];
int nex[MX], ans[MX], v[MX], seq[MX];
int main(void)
{
	in(n);
	for (R int i = 1; i <= n; ++i) in(nex[i]);
	for (R int i = 1; i <= n; ++i)
	{
		if (!vis[i])
		{
			++tot;
			int cur = i;
			vec.clear();
			vis[i] = true;
			vec.push_back(i);
			W (!vis[nex[cur]])
			{
				vis[nex[cur]] = true;
				vec.push_back(nex[cur]);
				cur = nex[cur];
			}
			dat[tot] = {vec.size(), vec};
		}
	}
	std::sort(dat + 1, dat + 1 + tot);
	R int cur = 1;
	W (cur <= tot)
	{
		if (dat[cur].siz & 1)
		{
			int half = dat[cur].siz + 1 >> 1;
			for (R int i = 1; i <= half; ++i) seq[(i << 1) - 1] = dat[cur].ele[i - 1];
			for (R int i = half + 1; i <= dat[cur].siz; ++i) seq[(i - half) << 1] = dat[cur].ele[i - 1];
			for (R int i = 1; i < dat[cur].siz; ++i) ans[seq[i]] = seq[i + 1];
			ans[seq[dat[cur].siz]] = seq[1];
			++cur;
		}
		else
		{
			if (cur == tot || dat[cur + 1].siz ^ dat[cur].siz) return puts("-1"), 0;
			int siz = dat[cur].siz, len = 0;
			for (R int i = 0; i < siz; ++i) seq[++len] = dat[cur].ele[i], seq[++len] = dat[cur + 1].ele[i];
			for (R int i = 1; i < len; ++i) ans[seq[i]] = seq[i + 1];
			ans[seq[len]] = seq[1];
			cur += 2;
		}
	}
	for (R int i = 1; i <= n; ++i) printf("%d ", ans[i]);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值