bzoj 3990: [SDOI2015]排序 dfs

来源:互联网 发布:2017最新拼图软件 编辑:程序博客网 时间:2024/05/22 09:45

题意

小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次操作,我们把序列分成2ni块,每块大小为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[n];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[n];i++) scanf("%d",&a[i]);    dfs(1,0);    printf("%lld",ans);    return 0;}
原创粉丝点击