codeforces 414 c

来源:互联网 发布:区域生长 分割算法 编辑:程序博客网 时间:2024/05/17 12:51

题意:

给出一个长度为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
原创粉丝点击