CodeForces 1269E K Integers 解题报告 树状数组 二分 逆序对

CodeForces 1269E K Integers 解题报告 树状数组 二分 逆序对

解题思路:树状数组,逆序对,二分。题意是给一组数,让你依次求在数组中排出1到k的子序列的最少交换次数,(k从1变化到n,每次加1),一开始我把他当成简单的逆序对升序排列做了,就wa了。这道题多一个步骤,要多加上将1到k这些数靠在一起的代价。
终于有点算法的味道了,好难。
在这里插入图片描述

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#pragma warning(disable:4996)
#define INF 0x3f3f3f3f
#define ll long long
#define PI acos(-1.0)
const int N = 1000010;
const int maxn = 1e9;
using namespace std;
ll a[200005];
ll sum[200005];
ll sum2[200005];
ll pos[200005];
ll n;
ll lowbit(ll x)
{
	return x &(-x);
}
void add(ll sum[], ll x, ll v)
{
	while (x <= n)
	{
		sum[x] += v;
		x += lowbit(x);
	}
}
ll query(ll sum[], ll x)
{
	ll res = 0;
	while (x > 0)
	{
		res += sum[x];
		x -= lowbit(x);
	}
	return res;
}
int main()
{
	scanf("%lld", &n);
	for (int i = 1; i <= n; i++)
	{
		scanf("%lld", &a[i]);
		pos[a[i]] = i;//记录每个数的初始下标,即在数组中的位置
	}
	ll ansl = 0;
	for (int i = 1; i <= n; i++)
	{
		ansl += i - 1 - query(sum, pos[i]);//这里求逆序对
		add(sum, pos[i], 1);//这棵树用来求逆序对
		add(sum2, pos[i], pos[i]);//这棵树用来求将数靠在一起需要的代价
		ll mid;
		ll l = 1, r = n;
		while (l <= r)
		{
			mid = (l + r) >> 1;
			if (query(sum, mid) * 2 <= i)
			{
				l = mid + 1;
			}
			else
			{
				r = mid - 1;
			}
		}
		ll ansr = 0;//这里求将1到k的数靠在一起需要的代价
		ll cnt = query(sum, mid), all = query(sum2, mid);
		ansr += mid * cnt - all - cnt * (cnt - 1) / 2;
		cnt = i - cnt, all = query(sum2, n) - all;
		ansr += all - cnt * (mid + 1) - cnt * (cnt - 1) / 2;
		printf("%lld ", ansl + ansr);
	}
}



你可能感兴趣的:(CodeForces 1269E K Integers 解题报告 树状数组 二分 逆序对)