do_while_true

一言(ヒトコト)

「题解」Codeforces 1268C K Integers

对于 \(f(k)\) 的计算,先计算把 \(1\sim k\) 移成连续段,然后再计算逆序对数(最小交换次数使得一个排列排好序)。

至于为什么是对的?感性理解一下,首先排好序一定要有个逆序对的代价,假如两个数之间没有相邻,则交换它们需要一步一步走过去,这样的花费就高了。

考虑 \(f(k-1)\to f(k)\) 新增的代价,计算逆序对数是容易的,\(k\) 只可能作为逆序对的第一个数,而第二个数可能的取值 \([1,k-1]\) 都已经出现过,所以直接计算全局中以 \(k\) 为开头的逆序对数,树状数组易做到。

计算把 \(1\sim k\) 移成连续段的代价,有个经典结论就是移到中位数处,如果长度为偶数,移到中间两个的哪个都行。

因为每个数不能移动到同一位置,所以代价计算会很麻烦,那就钦点让每个数可以移动到同一位置,最后再减去多余代价也就是两个等差数列即可。

现在计算每个数移动到中位数处的距离,注意到每加入一个 \(k\) 之后中位数的最多会移动 \(1\) 个位置,且移动之后除了 \(k\) 以外的值对总代价的贡献不变,直接用 set 维护 \(1\sim k\) 出现的位置然后移动就可以了。

其实可以不用维护中位数,直接用树状数组记录哪些位置出现了数然后倍增找中位数也可以,常数更小。

代码可能不大像说人话的样子,看看思路就好了。

#include<iostream>
#include<cstdio>
#include<vector>
#include<set>
#define pb push_back
#define mp std::make_pair
#define fir first
#define sec second
typedef std::pair<int, int> pii;
typedef std::vector<int> veci;
typedef std::vector<pii> vecpii;
typedef long long ll;
ll Abs(ll x) { return x < 0 ? -x : x; }
template <typename T>
T& read(T& r) {
	r = 0; bool w = 0; char ch = getchar();
	while(ch < '0' || ch > '9') w = ch == '-' ? 1 : 0, ch = getchar();
	while(ch >= '0' && ch <= '9') r = r * 10 + (ch ^ 48), ch = getchar();
	return r = w ? -r : r;
}
inline int lowbit(int x) { return x & (-x); }
const int N = 2000100;
int n, a[N], pos[N];
struct BIT {
	int tree[N];
	int sumq(int x) { int sum = 0; for(; x; x -= lowbit(x)) sum += tree[x]; return sum; }
	void modify(int x, int v) { for(; x <= n; x += lowbit(x)) tree[x] += v; }
}tr;
ll ans[N], c[N];
std::set<int>st;
ll calc(ll x) { return x * (x + 1) / 2; } 
signed main() {
	read(n);
	for(int i = 1; i <= n; ++i) read(a[i]), pos[a[i]] = i;
	for(int i = n; i; --i) {
		c[a[i]] = tr.sumq(a[i]);
		tr.modify(a[i], 1);
	}
	int p = 1, num = 1; st.insert(pos[1]);
	for(int i = 2; i <= n; ++i) {
		ans[i] = ans[i-1] + c[i];
		if(!(i&1)) {
			ans[i] += Abs(pos[num] - pos[i]);
			if(pos[i] < pos[num]) ++p;
			st.insert(pos[i]);
			continue ;
		}
		st.insert(pos[i]);
		if(pos[i] < pos[num]) ++p;
		if(p != (i+1)/2) {
			auto it = st.lower_bound(pos[num]);
			if(pos[i] < pos[num]) --it, --p;
			else ++it, ++p;
			num = a[*it];
		}
		ans[i] += Abs(pos[num] - pos[i]);
	}
	for(int i = 1; i <= n; ++i) printf("%lld ", ans[i] - calc((i-1)/2) - calc(i/2));
	return 0;
}
posted @ 2021-09-22 21:23  do_while_true  阅读(32)  评论(0编辑  收藏  举报