您的位置:首页 > 编程语言 > C语言/C++

BZOJ 3990 [SDOI2015] 排序

2017-02-17 20:30 218 查看

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.

Source

Round
1 感谢ZKY制作非官方数据

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

dfs+神奇的思路~

orzPoPoQQQ,orz黄学长……

思路详见黄学长的博客:http://hzwer.com/6839.html~

#include<cstdio>
#include<iostream>
using namespace std;

int n,a[5005],fac[13],bin[20];
long long ans;

bool che(int u,int v)
{
for(int i=1;i<v;i++) if(a[i+u]!=a[i+u-1]+1) return 0;
return 1;
}

void swapp(int u,int v,int k)
{
for(int i=0;i<bin[k];i++) swap(a[u+i],a[v+i]);
}

void dfs(int u,int v)
{
if(u==n+1)
{
ans+=fac[v];return;
}
int k1=0,k2=0;
for(int i=1;i<=bin
;i+=bin[u])
if(!che(i,bin[u]))
{
if(!k1) k1=i;
else if(!k2) k2=i;
else return;
}
if(!k1 && !k2) dfs(u+1,v);
else if(!k2)
{
swapp(k1,k1+bin[u-1],u-1);dfs(u+1,v+1);swapp(k1,k1+bin[u-1],u-1);
}
else
{
for(int x=0;x<=1;x++)
for(int y=0;y<=1;y++)
{
swapp(k1+x*bin[u-1],k2+y*bin[u-1],u-1);
if(che(k1,bin[u]) && che(k2,bin[u]))
{
dfs(u+1,v+1);
swapp(k1+x*bin[u-1],k2+y*bin[u-1],u-1);
break;
}
swapp(k1+x*bin[u-1],k2+y*bin[u-1],u-1);
}
}
}

int main()
{
fac[0]=bin[0]=1;
for(int i=1;i<20;i++) bin[i]=bin[i-1]<<1;
for(int i=1;i<=12;i++) fac[i]=fac[i-1]*i;
scanf("%d",&n);
for(int i=1;i<=bin
;i++) scanf("%d",&a[i]);
dfs(1,0);
printf("%lld\n",ans);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  C++ dfs 思路