Codeforces_414C:Mashmokh_and_Reverse_Operation(想法题)

来源:互联网 发布:淘宝返现处罚 编辑:程序博客网 时间:2024/06/05 11:00

题意大致是说给定2^n个数字,与m次操作,每次操作包含一个数字q,表示先将原数列每隔2^q个数字分成一组,共分为2^(n-q)组,之后再将每组内的数字取逆序,比如1 2 3取逆序后就变成3 2 1,要求每组操作之后输出整个包含2^n个数字的序列的逆序对个数.

解法是先归并排序预处理f[n][]求逆序对和顺序对,其中F[n][0]表示在归并过程中归并两组2^n长度的序列时得到的逆序对个数,f[n][1]为对应的顺序对个数,相等情况特殊处理.

由于在每隔2^q个数就将原序列逆置一次,如果设想还原之前归并排序的过程,那么很容易就能发现不论当前操作数为q的操作进行与否,当完成长度为2^q及更短的序列的归并过程再归并两个长度2^q序列时,对应的两个长为2^q的序列的状态时唯一确定的,即是已经排好序的状态,所以在这层及更上层的归并求得的逆序对/顺序对的个数是不变的,也就是这轮操作对于两组2^Q(Q>=q)长的序列归并的过程是没有影响.而在归并两个长为2^Q(Q<q)的序列的时候,因为当前操作q,所有长为2^q都逆置了,所以很容易就能发现,在求2^Q(Q<q)的情况时,所有逆序对都变为了顺序对,原本的顺序对也变为了逆序对,而这两个值也均以预处理求出.到这里,问题便已解决,细节详见程序.

#include<iostream>#include<stdio.h>#include<string.h>#include<stdlib.h>#include<math.h>using namespace std;#define ll long longconst double eps=0.00000001;ll n,m,a[(1<<20)+1],f[21][2],b[(1<<20)+1],ans,c[21];int lg2(int x){return (int)(log(x*1.0)/log(2.0)+eps);}void merge(int left,int right){if(left==right)return;int mid,i,j,k,now;mid=(left+right)>>1;now=lg2(right-mid);merge(left,mid);merge(mid+1,right);i=left;j=mid+1;while(i<=mid&&j<=right){if(a[i]<a[j]){f[now][1]+=right-j+1;i++;}else j++;}i=left;j=mid+1;k=left;while(i<=mid&&j<=right){if(a[i]>a[j]){f[now][0]+=mid-i+1;b[k++]=a[j++];}else b[k++]=a[i++];}while(i<=mid)b[k++]=a[i++];while(j<=right)b[k++]=a[j++];for(i=left;i<=right;i++)a[i]=b[i];return;}int main(void){int i,k;scanf("%I64d",&n);for(i=1;i<=(1<<n);i++)scanf("%I64d",a+i);merge(1,(1<<n));ans=0;for(i=0;i<=n;i++)ans+=f[i][0];scanf("%d",&m);while(m--){scanf("%d",&k);while(k--){ans=ans-f[k][c[k]]+f[k][1-c[k]];c[k]=1-c[k];}printf("%I64d\n",ans);}return 0;}


0 0