bzoj3990 [SDOI2015]排序 dfs

73 篇文章 0 订阅
11 篇文章 0 订阅

考试遇见这种题目只能傻眼。。
题目大意:给定一个长度为2^n的排列,有n个操作,第i个操作为【将序列分成2^(n-i+1)段,每段长2^(i-1),然后任选两段交换】,每个操作最多用一次,求有多少操作序列能把序列排出来
(orz popoqqq)

orz hzwer:
我们可以发现一个操作序列如果改换顺序,结果不变,所以我们只要找到一种以后把答案加上这个操作序列的阶乘就好了。
我们从小到大DFS,对于第i次操作我们将序列分成2^(n-i)段,每段长度2^i。
然后如果发现有两个以上的段不连续那么这很明显没用。
如果没有连续的段,那么不操作。
如果有一个这样的段,判断将这个段的前半部分和后半部分交换后是否连续递增,如果是就交换然后继续DFS

如果有两个这样的段,判断四种交换情况然后DFS

#include<cstdio>
#include<algorithm>
#include<cstring>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
const int N=1e5+5;
typedef long long ll;
int n,bin[15],fac[15],a[N];
ll ans;
bool check(int x,int y)
{
    fo(i,1,y-1)
    if (a[x+i-1]+1!=a[x+i])return 1;
    return 0;
}
inline void swap(int x,int y,int len)
{
    fo(i,0,len-1)
    swap(a[x+i],a[y+i]);
}
inline 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
    {
        fo(i,0,1)
        fo(j,0,1)
        {
            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;
    fo(i,1,n)bin[i]=bin[i-1]*2,fac[i]=fac[i-1]*i;
    fo(i,1,bin[n])scanf("%d",&a[i]);
    dfs(1,0);
    printf("%lld\n",ans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值