这个题很劲啊。搞了我一下午。。
大意是:给你一个
2n
长度的随意的数组,有
m
次查询,每次从左到右把数组依次分成长度为qi的块,将其
reverse
,问每次翻转之后逆序对数多少
比较难想的一道题。有些逆序对的结论很显然,设一段区间
seg
的逆序对数为
a
那么翻转这个区间之后的逆序对数为C2|seg|−a当然不能有重复的数出现,如果有重复的数出现,还需要减去
∑C2vi
其中
vi
表示
i
在区间出现的次数好的有了这些结论。其实并没有什么卵用。。。递归的思路是显然的,我们仿照归并排序的思路,先把左右区间搞出来,然后在计算出左区间对右区间的贡献值。可以在n∗2n复杂度预处理出来。然后重点就是查询了。一开始我更新到每个节点,设置了懒惰元素。但是不是
tle
就是
wa
。
之所以说这个题很劲,就是需要很抽象的想法才能搞掉。我们想想对于每一层
i
,设最开始在0层,那么这层有2i个小区间。每个小区间都对答案有一个独立的贡献
v
,v表示这个小区间左半部分对其右半部分的贡献,我们把这一层的小区间贡献的和求出来为
dp1[i]
,每当我们翻转这一层的时候,每个小区间都翻转了,但是每个小区间都是独立的,翻转就是一起翻转,于是再直接(归并倒着排)处理处翻转的和
dp2[i]
每次翻转就是交换
dp1,dp2
。再想想,翻转第
i
层,只会影响更大的层数,而不会影响之前的层数(这是因为翻转每个小区间,但是每个小区间的相对位置没变,而较小的层是左区间对右区间的贡献,左右相对位置没变)。
然后每次查询暴力更新最多n层,在求和输出即可
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <map>
#include <queue>
#include <string>
#include <sstream>
#include <set>
#include <vector>
#include <stack>
#define ALL(x) x.begin(), x.end()
#define INS(x) inserter(x, x,begin())
#define ll long long
#define CLR(x) memset(x, 0, sizeof x)
using namespace std;
const int inf = 2e9;
const int MOD = 1e9 + 7;
const int maxn = (1 << 20) + 10;
const int maxv = 1e3 + 10;
const double eps = 1e-9;
ll dp1[21], dp2[21];
int a[maxn], ta[maxn], tb[maxn];
int n, m, tot;
void build(int head, int tail, int deep) {
if(deep == n) {
dp1[deep] = dp2[deep] = 0;
return;
}
int mid = head + tail >> 1;
build(head, mid, deep+1);
build(mid, tail, deep+1);
int tota = 0, totb = 0;
for(int i = head; i < mid; i++) {
ta[tota++] = a[i];
}
for(int i = mid; i < tail; i++) {
tb[totb++] = a[i];
}
ta[tota] = tb[totb] = inf;
int la = 0, lb = 0;
ll ret = 0, retv = 0;
for(int i = head; i < tail; i++) {
if(tb[lb] <= ta[la]) lb++;
else {
retv += totb - lb;
la++;
}
}
la = lb = 0;
for(int i = head; i < tail; i++) {
if(ta[la] <= tb[lb]) a[i] = ta[la++];
else {
a[i] = tb[lb++];
ret += tota - la;
}
}
dp1[deep] += ret;
dp2[deep] += retv;
}
int main() {
#ifdef LOCAL
freopen("C:\\Users\\Administrator\\Desktop\\in.txt", "r", stdin);
freopen("C:\\Users\\Administrator\\Desktop\\out.txt","w",stdout);
#endif
scanf("%d", &n);
tot = 1 << n;
std::vector<int> xs;
for(int i = 0; i < tot; i++) {
scanf("%d", a + i);
}
build(0, tot, 0);
scanf("%d", &m);
while(m--) {
int q;
scanf("%d", &q);
ll sum = 0;
q = n - q;
for(int i = q; i <= n; i++) swap(dp1[i], dp2[i]);
for(int i = 0; i <= n; i++) sum += dp1[i];
printf("%lld\n", sum);
}
return 0;
}