Codeforces 414C Mashmokh and Reverse Operation 归并排序在线求交换序列后逆序数

36 篇文章 1 订阅
16 篇文章 0 订阅

题目链接: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;
}





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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值