[分治] [线段树] CF414C

posted on 2024-02-23 06:10:20 | under 未分类 | source

考虑反转区间的影响,显然并不会对区间外、跨越区间内外的逆序对造成影响,这只会将区间内的逆序对数量变成正序对数量。

考虑维护每个大小为 \(2\) 整数幂区间的正序对数量 \(cnt_1\)、逆序对数量 \(cnt_0\),显然反转一个区间后 \(cnt_1,cnt_0\) 互换,同时会将它的左子区间和右子区间调换。并也对子区间进行上述反转操作。

又因为此题特殊性,同样大小的区间一定会同时被反转,所以并不需要将每个区间的左子区间和右子区间调换,只考虑其正逆序对数量即可。那么整体上看就是将所有长度为 \(2^q\)\(2^{q-1}\)\(2^{q-2}\)……\(2^0\) 的区间都调换其正逆序对数量。

因此只需预处理出每一层的正逆序对数量,操作时暴力维护每一层的正逆序对数量即可。复杂度 \(O(n2^n)\)

代码

#include<bits/stdc++.h>
using namespace std;

#define mid ((l + r) >> 1)
const int N = (1 << 20) + 5;
int n, t, q, a[N], b[N];
long long cnt[25][2], ans;

inline int read() { int x = 0, w = 0; char ch = 0; while (!isdigit(ch)) {w |= ch == '-'; ch = getchar();} while (isdigit(ch)) {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();} return w ? -x : x; }
inline void write(long long x) { if (x < 0) putchar('-'), x = -x; if (x > 9) write(x / 10); putchar(x % 10 + '0'); }
inline void init(int h){
	if(!h) return ;
	init(h - 1);
	for(int l = 1, r; l <= 1 << n; l += 1 << h){
		r = l + (1 << h) - 1;
		for(int i = l, j = mid + 1, k = l; k <= r; ++k)
			if((j > r || a[i] < a[j]) && i <= mid) b[k] = a[i++];
			else b[k] = a[j++];
		for(int i = l, j = mid + 1; i <= mid; ++i){
			while(j <= r && a[j] < a[i]) ++j;
			cnt[h][0] += j - mid - 1;
		}
		for(int i = l, j = mid + 1; j <= r; ++j){
			while(i <= mid && a[i] < a[j]) ++i;
			cnt[h][1] += i - l;
		}
	}
	ans += cnt[h][0];
	for(int i = 1; i <= 1 << n; ++i) a[i] = b[i]; 
}
signed main(){
	cin >> n;
	for(int i = 1; i <= 1 << n; ++i) a[i] = read();
	init(n);
	cin >> t;
	while(t--){
		q = read();
		for(q; ~q; --q)
			ans += cnt[q][1] - cnt[q][0], swap(cnt[q][0], cnt[q][1]);
		write(ans), putchar('\n');
	}
	return 0;
}
posted @ 2026-01-12 20:13  Zwi  阅读(2)  评论(0)    收藏  举报