[分治] [线段树] 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;
}

浙公网安备 33010602011771号