题目链接:http://codeforces.com/contest/414/problem/C
先复制一个思路:
解法: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各个区间翻转后的逆序数就可以了。
题意:
给定 2^n 长的序列
下面que个询问,对于每个询问 u,
1、先把 序列分段分成: [0, 2^u-1], [2^u, 2^(u+1)-1],······
2、再把每段都翻转一下。
3、输出此时序列的逆序数。
思路:首先我们要得到:
对于一个序列[l,r) 的翻转 =
1、 将该序列的[l,mid), [mid,r)}交换
2、[l,mid/2),[mid/2,mid)交换 + [mid,mid+mid/2),[mid+mid/2,r) 交换
3、 如此递归下去
例如:12345678-> 87654321
等价于 1234 | 5678 -> 5678 | 1234 -> 7856 | 3412 -> 8765 | 4321
于是把序列分层,得到n层
对于每一层[l, r] 的逆序数对分为:
1、两个数字都在[l,mid) 内的 +两个数字都在 [mid,r]内的
2、一个数字在[l,mid),一个数字在[mid,r]
3、若区间内只有2个数则只有第二种情况,第一种情况答案为0.
所以累加上每层的区间之间的逆序数即为整个序列的逆序数
而交换一下区间就是 2、中结果变成 : 一个数字在[mid,r] ,一个数字在[l,mid )
即每层的2有2种答案。
预处理出每层的2个答案,累加即可。
#include <iostream> #include <cstdio> #include <algorithm> #include <cstring> #include <string> using namespace std; #define N 20 #define ll __int64 ll a[1 << N], n, m; //一个序列[l,r) 的翻转 = 将该序列的{[l,mid), [mid,r)} 交换 + ({[l,mid/2),[mid/2,mid)} + {[mid,mid+mid/2),[mid+mid/2,r)} ) + 如此递归下去 ll sum[N + 1][2]; //sum[deep]表示 求和每段[l,r]的:[l,mid)和[mid,r) 之间的逆序数 void dfs(ll l, ll r, ll deep){ if(l+1 >= r)return ; ll mid = (l+r)/2; dfs(l, mid, deep-1); dfs(mid, r, deep-1); ll cnt = 0; for(ll i = l; i < mid; i++) cnt += lower_bound(a+mid, a+r, a[i]) - (a+mid); sum[deep][0] += cnt; cnt = 0; for(ll i = mid; i < r; i++) cnt += lower_bound(a+l, a+mid, a[i]) - (a+l); sum[deep][1] += cnt; inplace_merge(a+l, a+mid, a+r); } int main(){ ll n, i, que, u; ll er[30];er[0] = 1; for(i=1;i<30;i++)er[i] = er[i-1]*2; while(~scanf("%I64d",&n)){ memset(sum, 0, sizeof sum); for(i = 0; i < er[n]; i++)scanf("%I64d",&a[i]); dfs(0, er[n], n); scanf("%I64d",&que); while(que--) { scanf("%I64d",&u); ll ans = 0; while(u--)swap(sum[u+1][0], sum[u+1][1]); for(i=0; i <= n; i++) ans += sum[i][0]; printf("%I64d\n",ans); } } return 0; }