codeforces 414 c

题意:

给出一个长度为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层的反向逆序数。反转只是将其值进行交换。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值