您的位置:首页 > 其它

[SDOI2015][BZOJ3990] 排序

2015-05-14 16:19 218 查看

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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: