题意:给你2^n个数,每次操作将其分成2^k份,对于每一份内部的数进行翻转,每次操作完后输出操作后的2^n个数的逆序数。
解法:2^n个数,可以联想到建立一棵二叉树的东西,比如 2,1,4,3就可以建成下面这样
[2,1,4,3] level 2
/
[2,1] [4,3] level 1
/ /
[2] [1] [4] [3] level 0
然后算某个区间的逆序数的时候[2,1,4,3]实际上就是算[2,1],[4,3]的逆序数再加上[2,1],[4,3]之间的逆序数,思路和归并排序是一样的。
然后我们看下每次翻转,假如我们分成2^k份,我们会发现,对于level k+1层以上的逆序数是不会改变的,但是level k~level 0的逆序数对会翻转,我们只需要知道level k~level 0各个区间翻转后的逆序数就可以了。
所以做法就有点类似于归并求逆序数,对于每个结点,记inv[0]为不翻转时的逆序数,inv[1]为翻转的时候的逆序数,然后我们记sum[k][0]和sum[k][1] 为该层所有结点的inv[0]的和 以及inv[1]的和。 然后每次操作就是将 sum[k~0]的0,1值交换,然后逆序数就是sum[n~0][0]的和。
#pragma warning(disable:4996) #include<iostream> #include<cstring> #include<string> #include<cstdio> #include<algorithm> #include<vector> #include<cmath> #define ll long long #define maxn 2000000 using namespace std; ll a[maxn]; int n; ll sum[25][2]; void build(int dep,int L, int R) { if (dep == 0){ sum[dep][0] = sum[dep][1] = 0; return; } int M = (L + R) >> 1; build(dep - 1, L, M); build(dep - 1, M + 1, R); for (int i = L; i <= M; i++){ sum[dep][1] += a+R+1-upper_bound(a + M + 1, a + R + 1, a[i]); sum[dep][0] += lower_bound(a + M + 1, a + R+1, a[i]) - (a + M + 1); } sort(a + L, a + R + 1); }; int main() { while (cin >> n) { memset(sum, 0, sizeof(sum)); int tot = 1 << n; for (int i = 1; i <= tot; i++){ scanf("%I64d", a + i); } build(n, 1, tot); int m, q; cin >> m; for (int i = 0; i < m; i++){ scanf("%d", &q); for (int j = q; j >= 0; j--){ swap(sum[j][0], sum[j][1]); } ll ans = 0; for (int j = n; j >= 0; j--){ ans += (ll)sum[j][0]; } printf("%I64d ", ans); } } return 0; }