D. Yet Another Sorting Problem(树状数组求逆序对 + 奇偶排列)

Problem - D - Codeforces

Petya有一个整数数组a1 a2, an-他只喜欢排序数组。不幸的是,给定的数组可能是任意的,所以Petya想对它进行排序。Petya喜欢挑战自己,所以他想只用3个循环来排序数组。更正式地说,在一个操作中,他可以选择3个成对不同的指标i, j和k(1≤i, j, k≤n),并对数组a应用i→j→k→icycle。它同时将a放在位置j上,aj放在位置k上,ak放在位置i上,而不改变任何其他元素。例如,如果a是[10,50,20,30,40,60],他选择i = 2, j = 1, k = 5,那么数组就变成[50,40,20,30,10,60]。Petya可以应用任意数量的3-cycle(可能是0)。你要确定彼佳是否能对他的数组a排序,即使它是非递减的。输入每个测试包含多个测试用例。第一行包含测试用例的数量t (1 < t < 5 - 105)。测试用例的描述如下。每个测试用例的第一行包含一个整数n (1 <n <5- 105)——数组a的长度。每个测试用例的第二行包含n个整数a1, a2,…, an(1≤ai≤n)。它保证所有测试用例的n的和不超过5 - 105。输出对于每个测试用例,如果Petya可以使用3个循环对数组a排序,则打印“YES”(不带引号),否则打印“NO”(不带引号)。您可以在任何情况下打印每个字母(大写或小写)。

Example

input

Copy

7
1
1
2
2 2
2
2 1
3
1 2 3
3
2 1 3
3
3 1 2
4
2 1 4 3

output

Copy

YES
YES
NO
YES
NO
YES
YES

题解:
在写本道题前,我们应该清楚一些定义

定义:降序次数为偶数的排列为偶排列;降序次数为奇数的排列为奇排列。

例如排列(2,3,1):从左往右看,2与其后元素相比有降序,即2大于1;3与其后元素相比有降序,即3大于1;1无降序;则排列降序次数为2次,因此为偶排列。

例如排列(1,3,2):从左往右看,1与其元素无降序,3与其后元素有降序,2无降序;因此其排列降序次数为1,为奇排列。

假设原位置1 , 2 , 3

按题目规则排完只有两种形式,
2 3 1 (逆序对数为 2)

3 1 2 (逆序对数为 2)

按照题目中所给规则,一次改变会将会将逆序对的数量变化2,所以我们只需要利用树状数组,看逆序对数目是否为偶数即可

还有一种特殊情况是,假如有两个数相等的情况,一定是可以的

我们刚才的考虑情况是默认三个数均不同的,发现如果三个数里存在一样的,逆序对就可能产生奇数变化,这样的话一定有解了。

#include<iostream>
#include<string>
#include<vector>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
int a[500050];
int f[500050];
int tre[500050];
int lowbit(int x)
{
	return x & - x;
}
int sum(int x)
{
	int res = 0;
	for(int i = x;i ;i -= lowbit(i))
	res += tre[i];
	return res;
}
void add(int x,int n)
{
	for(int i = x;i <= n;i += lowbit(i))
	tre[i] ++;
}
void solve() 
{
	int n;
	scanf("%d",&n);
	for(int i = 1;i <= n;i++)
	f[i] = 0,tre[i] = 0;
	int ff = 0;
	for(int i = 1;i <= n;i++)
	{
		scanf("%d",&a[i]);
		if(f[a[i]])
		{
			ff = 1;
		}
		f[a[i]]++;
	}
	if(ff)
	{
		printf("YES\n");
		return ;
	}
	
	int res = 0;
	
	for(int i = 1;i <= n;i++)
	{
		res += sum(n) - sum(a[i]);
		add(a[i],n);
	}
	
	if(res&1)
	{
		printf("NO\n");
	}
	else
	{
		printf("YES\n");
	}
}
 
//1 2 4
signed main() 
{
//	ios::sync_with_stdio(0);
//	cin.tie(0);cout.tie(0);
	int t = 1;
//	cin >> t;
    scanf("%d",&t);
	while (t--) 
	{
		solve();
	}
	return 0;
}
//1 1 1 0 1
 
//1 1 1 0 1
//1 1 1 0 1
//1 1 1 0 1
//0 1 1 1 1
//0 1 1 1 1

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值