D. Lucky Permutation codeforces1768D

72 篇文章 0 订阅
23 篇文章 0 订阅

Problem - D - Codeforces

题目大意:给出一个长度为n的排列a,每次操作可以交换两个数,问最少几次操作能使排列中有且仅有一个逆序对

2<=n<=2e5;

思路:有且只有一个逆序对的情况就是将这个排列从小到大排序,然后在交换两个相邻数,所以我们先考虑把排列排序,就需要用到置换环,对于任意的i属于1到n从i到ai建一条有向边,例如2,3,4,1,5这个排列的置换环如下:

如果我们交换2,1使1被放到排序好的位置,就相当于将1的出边指向自己,然后4的出边指向2,类似于链表删除元素的操作,所以对于有四个元素的环,3次操作就能使所有数排好序,所以要使整个列排好序只需要n-环的数量次操作,然后要有一个逆序对只需再额外交换两个相邻数即可,但是在排序的过程可能出现过两个相邻元素成一个点数为2的环,这样的环不需要拆,操作数不需要+1,而需要-1,所以我们对于在dfs时,对于每个环都用map记录当前访问过的元素,如果遇到过+1或-1的元素,就操作数-1

//#include<__msvc_all_public_headers.hpp>
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
int e[N];
bool vis[N];
bool flag;
map<int, int>ma;
void dfs(int u)
{//遍历环
	if (vis[u])
		return;
	vis[u] = 1;	
	ma[u] = 1;//记录在环中出现过的点的编号
	if (ma[u - 1] || ma[u + 1])
		flag = 1;//出现过相邻点
	dfs(e[u]);
}
int main()
{
	cin.tie(0);
	ios::sync_with_stdio(false);
	int t;
	cin >> t;
	while (t--)
	{
		int n;
		cin >> n;
		for (int i = 1; i <= n; i++)
		{
			vis[i] = 0;//用于dfs避免重复遍历以及判环
		}
		flag = 0;..判断是+1还是-1
		for (int i = 1; i <= n; i++)
		{
			int x;
			cin >> x;	
			e[i] = x;//单向边
		}
		int ans = 0;
		for (int i = 1; i <= n; i++)
		{
			if (vis[i])//这里一定要有,否则时间复杂度是O(n方)
				continue;
			dfs(i);
			ma.clear();
			ans++;//统计环的数量
		}
		if (!flag)
		{
			cout << n - ans + 1 << endl;
		}
		else
		{
			cout << n - ans - 1 << endl;
		}
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

timidcatt

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

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

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

打赏作者

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

抵扣说明:

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

余额充值