[SDOI2015][BZOJ3990] 排序

3990: [SDOI2015]排序

Time Limit: 20 Sec  Memory Limit: 128 MB
Submit: 175  Solved: 94
[Submit][Status][Discuss]

Description

 小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].
 

Input

第一行,一个整数N

第二行,2^N个整数,A[1..2^N]
 

Output

一个整数表示答案

 

Sample Input

3
7 8 5 6 1 2 4 3

Sample Output

6

HINT

 

100%的数据, 1<=N<=12.

 
考场上直接暴力35分……
分析:
定理1:
对于每种合法的操作序列,其序列顺序是无关的,所以求出一种合法的序列,求出他的全排列(即阶乘)就ok了,效率++;
因此我们可以从小到大操作。
定理2:
假如序列为 [3 6 1 2 7 8 5 4] ,我们要进行长度为1的段操作,那么长度为2的段中不满足递增的段的个数num,若num>2则无解,num=2,有四种交换方式,num=1,有一种交换方式,num=0,不用交换,继续递归。
 
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<vector>
using namespace std;
int n,a[4100],sum;
long long ans,jc[13];
void change(int l,int r,int val)
{
    for (int i=0;i<val;i++)
    {
        int t=a[l+i];
        a[l+i]=a[r+i];
        a[r+i]=t;
    }
}
void dfs(int x,int k)
{
    if (x==n)
    {
        ans+=jc[k];
        return;
    }
    int tot=0; 
    int b[3]={0,0,0};
    int val=1<<x;
    for (int i=1;i<=sum;i+=(val<<1))
        if (a[i+val-1]+1!=a[i+val])
        { 
            if (tot==2) return; 
            b[++tot]=i;
        }        
    if (tot==2)
    {
        if (a[b[1]+val-1]+1==a[b[2]+val]&&a[b[1]+val]-1==a[b[2]+val-1])
        {
            change(b[1],b[2],val);
            dfs(x+1,k+1);
            change(b[1],b[2],val);
            change(b[1]+val,b[2]+val,val);
            dfs(x+1,k+1);
            change(b[1]+val,b[2]+val,val);
        }
        if (a[b[1]+val]-1==a[b[2]+val*2-1]&&a[b[2]+val-1]+1==a[b[1]])
        {
            change(b[1],b[2]+val,val);
            dfs(x+1,k+1);
            change(b[1],b[2]+val,val);
        }
        if (a[b[1]+val-1]+1==a[b[2]]&&a[b[1]+val*2-1]==a[b[2]+val]-1)
        {
            change(b[1]+val,b[2],val);
            dfs(x+1,k+1);
            change(b[1]+val,b[2],val);
        }
        return; 
    }
    if (tot==1)
    {
        change(b[1],b[1]+val,val);
        dfs(x+1,k+1);
        change(b[1],b[1]+val,val);
        return;
    }
    if (tot==0) 
    {
        dfs(x+1,k);
        return;
    }
}
int main()
{
    scanf("%d",&n);    
    sum=1<<n;
    for (int i=1;i<=sum;i++) scanf("%d",&a[i]);
    jc[0]=1;
    for (int i=1;i<=n;i++) jc[i]=jc[i-1]*i;
    ans=0;
    dfs(0,0);
    printf("%d",ans);
    return 0;
}

 

posted @ 2015-05-14 16:19  ws_fqk  阅读(322)  评论(0编辑  收藏  举报