BZOJ3990:[SDOI2015]排序——题解

https://www.lydsy.com/JudgeOnline/problem.php?id=3990

小A有一个1-2^N的排列A[1..2^N],他希望将A数组从小到大排序,小A可以执行的操作有N种,每种操作最多可以执行一次,对于所有的

i(1<=i<=N),第i中操作为将序列从左到右划分为2^{N-i+1}段,每段恰好包括2^{i-1}个数,然后整体交换其中两段.小A想知道可以将数组A从小到

大排序的不同的操作序列有多少个,小A认为两个操作序列不同,当且仅当操作个数不同,或者至少一个操作不同(种类不同或者操作位置不同).

下面是一个操作事例:

N=3,A[1..8]=[3,6,1,2,7,8,5,4].

第一次操作,执行第3种操作,交换A[1..4]和A[5..8],交换后的A[1..8]为[7,8,5,4,3,6,1,2].

第二次操作,执行第1种操作,交换A[3]和A[5],交换后的A[1..8]为[7,8,3,4,5,6,1,2].

第三次操作,执行第2中操作,交换A[1..2]和A[7..8],交换后的A[1..8]为[1,2,3,4,5,6,7,8].

考虑暴力怎么写……不会啊。

我们能知道实际上操作顺序不影响结果,于是我们大可以从1~n枚举操作做。

因为最终变得有序,所以我们可以对于每一“大块”查询是否已经排好序了,如果没排好,就说明这一“大块”里的两小块肯定是放错位置的。

如果这样的小块<=4的话我们还好办,显然是前一“大块”的其中一“小块”和后一“大块”的其中一“小块”交换位置。

如果<=2那么就这两块交换就行了。

如果>4我们就处理不了了。

#include<map>
#include<cmath>
#include<stack>
#include<queue>
#include<cstdio>
#include<cctype>
#include<vector>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=1<<13;
inline int read(){
    int X=0,w=0;char ch=0;
    while(!isdigit(ch)){w|=ch=='-';ch=getchar();}
    while(isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    return w?-X:X;
}
int n,m,a[N];
ll jc[N],ans;
bool pan(int l,int r){
    for(int i=l;i<r;i++)
    if(a[i]+1!=a[i+1])return 0;
    return 1;
}
void mdy(int l,int r,int k){
    for(int i=l,j=r;i<l+(1<<k);i++,j++)
    swap(a[i],a[j]);
}
void dfs(int l,int num){
    if(l>n){
    ans+=jc[num];
    return;
    }
    int tmp[4],tot=0;
    for(int i=1;i<=m;i+=(1<<l))
    if(!pan(i,i+(1<<l)-1)){
        tmp[++tot]=i;
        if(tot>2)return;
    }
    if(!tot){dfs(l+1,num);return;}
    else if(tot==1){
    mdy(tmp[1],tmp[1]+(1<<l-1),l-1);
    if(pan(tmp[1],tmp[1]+(1<<l-1)))dfs(l+1,num+1);
    mdy(tmp[1],tmp[1]+(1<<l-1),l-1);
    }else{
    for(int i=0;i<=1;i++)
        for(int j=0;j<=1;j++){
        mdy(tmp[1]+(i<<(l-1)),tmp[2]+(j<<(l-1)),l-1);
        if(pan(tmp[1],tmp[1]+(1<<l-1))&&
            pan(tmp[2],tmp[2]+(1<<l-1)))dfs(l+1,num+1);
        mdy(tmp[1]+(i<<(l-1)),tmp[2]+(j<<(l-1)),l-1);
        }
    }
}
int main(){
    n=read(),m=1<<n;
    jc[0]=1;for(int i=1;i<=n;i++)jc[i]=jc[i-1]*i;
    for(int i=1;i<=m;i++)a[i]=read();
    dfs(1,0);
    printf("%lld\n",ans);
    return 0;
}

+++++++++++++++++++++++++++++++++++++++++++

+本文作者:luyouqi233。               +

+欢迎访问我的博客:http://www.cnblogs.com/luyouqi233/+

+++++++++++++++++++++++++++++++++++++++++++

posted @ 2018-06-20 09:48  luyouqi233  阅读(310)  评论(0编辑  收藏  举报