题目:
http://codeforces.com/contest/415/problem/E
思路:
查询次数很多,所以考虑预处理出每段的逆序对的个数,这里使用归并排序计算出每次一层归并所得的逆序对和正序对。在之后的每次查询时,把受到影响的部分减去,加上原来的正序对即是答案。
代码:
#include <cstdio>
using namespace std;
const int maxn = (1 << 20) + 5;
typedef long long ll;
#define FOR(i, a, b) for(int i = a; i <= b; i++)
int K, N, Q;
ll A[maxn], B[maxn];
ll INV[21][2];
bool H[21];
void init(int l, int r, int d) {
if(l == r) return;
int m = (l + r) >> 1;
init(l, m, d + 1);
init(m + 1, r, d + 1);
int rl = m + 1;
FOR(i, l, m) {
while(rl <= r && A[rl] < A[i]) rl++;
INV[d][0] += rl - m - 1;
}
rl = l;
FOR(i, m + 1, r) {
while(rl <= m && A[rl] < A[i]) rl++;
INV[d][1] += rl - l;
}
int ll = l, cur = l;
rl = m + 1;
while(ll <= m && rl <= r) {
if(A[ll] < A[rl]) B[cur++] = A[ll], ll++;
else B[cur++] = A[rl], rl++;
}
while(ll <= m) B[cur++] = A[ll], ll++;
while(rl <= r) B[cur++] = A[rl], rl++;
FOR(i, l, r) A[i] = B[i];
}
int main() {
ll sum = 0;
scanf("%d", &N);
K = 1 << N;
FOR(i, 1, K)
scanf("%lld", &A[i]);
init(1, K, 0);
FOR(i, 0, N) sum += INV[i][0];
scanf("%d", &Q);
while(Q--) {
int tmp;
scanf("%d", &tmp);
FOR(i, N - tmp, N)
sum = sum - INV[i][ H[i] ] + INV[i][ H[i] ^=1 ];
printf("%lld\n", sum);
}
return 0;
}