题意:
给出一个长度为2^n的数列,然后要将其分成每段长度为2^q的小段,将每段进行反转,问新数列的逆序数。每次操作都是建立在上一次的操作之上。
思想:
开始的时候进行了一次模拟,分段,反转,求逆序数,跪在了第10组上。数据是n=20
//
// main.cpp
// jixun
//
// Created by zhou yi on 14-4-8.
// Copyright (c) 2014年 edward. All rights reserved.
//
#include "stdio.h"
#include "string.h"
#include <iostream>
#include <algorithm>
#include <queue>
#include <stack>
#include <math.h>
using namespace std;
const int MAX = 10000000;
int num[MAX];
int aa[MAX];
int tt[MAX];
int ans;
void get_array(int i,int x)
{
reverse(num+i, num+i+x);
}
void msort(int s,int t)
{
if(s>=t)
return ;
int mid= (s+t)/2;
msort(s, mid);
msort(mid+1, t);
int i = s,j = mid+1,p = 0;
while(i <= mid && j <= t)
{
if (aa[i] <= aa[j])
{
tt[p++]=aa[i++];
}
else
{
tt[p++] = aa[j++];
ans += mid+1-i;
}
}
while (i <= mid)
{
tt[p++] = aa[i++];
}
while (j <= t)
{
tt[p++] = aa[j++];
}
for (int i = s,j = 0; i <= t; ++ i)
{
aa[i] = tt[j++];
}
}
int main()
{
int n;
scanf("%d",&n);
n = 1<<n;
for (int i = 0; i < n; ++ i)
{
scanf("%d",&num[i]);
}
int m;
scanf("%d",&m);
while (m--)
{
ans = 0;
int t = 0;
int q;
scanf("%d",&q);
q = 1 << q;
while (t < n)
{
get_array(t,q);
t+=q;
}
for (int i = 0; i < n; ++ i)
{
aa[i] = num[i];
}
msort(0, n-1);
printf("%d\n",ans);
}
return 0;
}
然后参考了一下网上的博客。发现讲的实在是简短……
首先,反转之后的逆序数,在反转之前是可以得到的(即把数列反过来看)。
然后,将数分解成2^q段,然后反转,其实相当于归并排序中的1-q层进行了反转(想想为什么,提示:归并排序是每次将数列分成两半),而反转后的逆序数在之前已经求到了,于是不需要进行归并排序,而只需要把所有归并排序的逆序数加起来就行了。
并且,归并排序还可以做简易的就可以了(每次都完完整整排序反而更加麻烦)。
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
typedef long long int LL;
int n,m,a[1<<21];
LL dp[21][2];
void merge_sort(int l,int r,int dep)
{
if(l>=r) return ;
int m=(l+r)/2;
merge_sort(l,m,dep-1); merge_sort(m+1,r,dep-1);
for(int i=l;i<=m;i++)
{
dp[dep][0]+=lower_bound(a+m+1,a+r+1,a[i])-(a+m+1);
dp[dep][1]+=r-m-(upper_bound(a+m+1,a+r+1,a[i])-(a+m+1));
}
sort(a+l,a+r+1);
}
int main()
{
scanf("%d",&n);
for(int i=1,sz=(1<<n);i<=sz;i++) scanf("%d",a+i);
merge_sort(1,(1<<n),n);
scanf("%d",&m);
while(m--)
{
int q;
LL ans=0;
scanf("%d",&q);
for(int i=1;i<=q;i++) swap(dp[i][0],dp[i][1]);
for(int i=1;i<=n;i++) ans+=dp[i][0];
printf("%I64d\n",ans);
}
return 0;
}
注:此代码采自网络,N多相同代码,不知道原出处,引用希作者见谅
使用dp[i][0]表示第i层的原逆序数,dp[i][1]表示第i层的反向逆序数。反转只是将其值进行交换。