[SDOI2015][BZOJ3990] 排序
3990: [SDOI2015]排序
Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 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
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; }