CF240 DIV1 C--归并排序求逆序

10 篇文章 0 订阅
Mashmokh and Reverse Operation
time limit per test
4 seconds
memory limit per test
512 megabytes
input
standard input
output
standard output

Mashmokh's boss, Bimokh, didn't like Mashmokh. So he fired him. Mashmokh decided to go to university and participate in ACM instead of finding a new job. He wants to become a member of Bamokh's team. In order to join he was given some programming tasks and one week to solve them. Mashmokh is not a very experienced programmer. Actually he is not a programmer at all. So he wasn't able to solve them. That's why he asked you to help him with these tasks. One of these tasks is the following.

You have an array a of length 2n and m queries on it. The i-th query is described by an integer qi. In order to perform the i-th query you must:

  • split the array into 2n - qi parts, where each part is a subarray consisting of 2qi numbers; the j-th subarray (1 ≤ j ≤ 2n - qi) should contain the elements a[(j - 1)·2qi + 1], a[(j - 1)·2qi + 2], ..., a[(j - 1)·2qi + 2qi];
  • reverse each of the subarrays;
  • join them into a single array in the same order (this array becomes new array a);
  • output the number of inversions in the new a.

Given initial array a and all the queries. Answer all the queries. Please, note that the changes from some query is saved for further queries.

Input

The first line of input contains a single integer n (0 ≤ n ≤ 20).

The second line of input contains 2n space-separated integers a[1], a[2], ..., a[2n] (1 ≤ a[i] ≤ 109), the initial array.

The third line of input contains a single integer m (1 ≤ m ≤ 106).

The fourth line of input contains m space-separated integers q1, q2, ..., qm (0 ≤ qi ≤ n), the queries.

Note: since the size of the input and output could be very large, don't use slow output techniques in your language. For example, do not use input and output streams (cin, cout) in C++.

Output

Output m lines. In the i-th line print the answer (the number of inversions) for the i-th query.

Sample test(s)
input
2
2 1 4 3
4
1 2 0 2
output
0
6
6
0
input
1
1 2
3
0 1 1
output
0
1
0
Note

If we reverse an array x[1], x[2], ..., x[n] it becomes new array y[1], y[2], ..., y[n], where y[i] = x[n - i + 1] for each i.

The number of inversions of an array x[1], x[2], ..., x[n] is the number of pairs of indices i, j such that: i < j and x[i] > x[j].


转自:http://blog.csdn.net/acmmmm/article/details/23189375

先复制一个思路:

解法: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; s

	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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值