您的位置:首页 > 其它

bzoj 3990: [SDOI2015]排序 dfs

2017-07-24 16:22 232 查看

题意

小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<=12

分析

orz题解。

注意到这题块与块之间是不会影响的,所以操作序列的顺序是没有关系的,所以我们只要从小到大搜最后乘上一个阶乘即可。

对于第i次操作,我们把序列分成2n−i块,每块大小为2i。定义符合条件的块为块内元素连续且递增的块,反之则为不符合条件的块。如果全都符合条件,则不用进行该操作;如果只有一块是不符合条件的,那么只要交换这块的前后两部分即可;否则就枚举可能的四种交换情况。

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;

typedef long long LL;

int n,bin[15],fac[15],a[5005];
LL ans;

bool check(int x,int y)
{
for (int i=1;i<y;i++)
if (a[x+i-1]+1!=a[x+i]) return 1;
return 0;
}

void swap(int x,int y,int len)
{
for (int i=0;i<len;i++) swap(a[x+i],a[y+i]);
}

void dfs(int x,int y)
{
if (x>n)
{
ans+=fac[y];
return;
}
int pos1=0,pos2=0;
for (int i=1;i<=bin
;i+=bin[x])
if (check(i,bin[x]))
{
if (!pos1) pos1=i;
else if (!pos2) pos2=i;
else return;
}
if (!pos1&&!pos2) dfs(x+1,y);
else if (!pos2)
{
swap(pos1,pos1+bin[x-1],bin[x-1]);
if (!check(pos1,bin[x])) dfs(x+1,y+1);
swap(pos1,pos1+bin[x-1],bin[x-1]);
}
else
{
for (int i=0;i<=1;i++)
for (int j=0;j<=1;j++)
{
swap(pos1+i*bin[x-1],pos2+j*bin[x-1],bin[x-1]);
if (!check(pos1,bin[x])&&!check(pos2,bin[x]))
{
dfs(x+1,y+1);
swap(pos1+i*bin[x-1],pos2+j*bin[x-1],bin[x-1]);
break;
}
swap(pos1+i*bin[x-1],pos2+j*bin[x-1],bin[x-1]);
}
}
}

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