• [SDOI2015]排序 题解 (搜索)


    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

    正解居然是搜索,考场上看这板儿B是个神仙状压就skip掉了

    后悔啊……把猛肝某APIO2016T1的时间放这题上怎么还没30分啊……

    手%几组数据可以发现,操作序列的合法性与顺序无瓜

    所以只需确定序列中有没有第i种操作,最后将统计结果的阶乘输出即为序列数

    枚举操作种数i,+1什么的太麻烦就直接分成$2^{N-i}$段,每段$2^i$个数

    然后要交换的话就需要找非严格递增序列($a_{x+1}!=a_x+1$)

    超过两个显然不可行,直接return

    接下来分类讨论:

    如果没有这样的序列,继续dfs

    如果有一个,尝试内部一分为二后交换使之满足严格递增

    如果有两个,两段分成四段尝试交换

    (感谢hzwer的题解 大大减少了我的代码量 两层for分类讨论确实比四个if else美观多辽)

    收获:看到二进制不要直接想状压,还有可能是树形结构或者二分搜索

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    using namespace std;
    const int N=(1<<12)+5;
    int n,a[N],tot;
    long long ans=0,bin[25],fac[25];
    int read()
    {
        int x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return x*f;
    }
    void ini()
    {
        bin[0]=fac[0]=1;
        for(int i=1;i<=20;i++)
            bin[i]=bin[i-1]<<1,fac[i]=1LL*i*fac[i-1];
    }
    bool judge(int p,int k)
    {
        for(int j=1;j<bin[k];j++)
            if(a[p+j]!=a[p+j-1]+1)return 0;
        return 1;
    }
    void sw_(int x,int y,int k)
    {
        for(int i=0;i<bin[k];i++)
            swap(a[x+i],a[y+i]);
    }
    void dfs(int p,int val)
    {
        if(p==n+1)
        {
            ans+=fac[val];
            return ;
        }
        int bl1,bl2;bl1=bl2=0;
        for(int i=1;i<=bin[n];i+=bin[p])
            if(!judge(i,p))
            {
                if(!bl1)bl1=i;
                else if(!bl2)bl2=i;
                else return ;
            }
        if(!bl1&&!bl2)dfs(p+1,val);
        else if(bl1&&!bl2)
        {
            sw_(bl1,bl1+bin[p-1],p-1);
            dfs(p+1,val+1);
            sw_(bl1,bl1+bin[p-1],p-1);
        }
        else 
        {
            for(int num1=0;num1<2;num1++)
                for(int num2=0;num2<2;num2++)
                {
                    sw_(bl1+num1*bin[p-1],bl2+num2*bin[p-1],p-1);
                    if(judge(bl1,p)&&judge(bl2,p))
                    {
                        dfs(p+1,val+1);
                        sw_(bl1+num1*bin[p-1],bl2+num2*bin[p-1],p-1);
                        break;
                    }
                    sw_(bl1+num1*bin[p-1],bl2+num2*bin[p-1],p-1);
                }
        }
    }
    int main()
    {
        n=read();
        ini();
        for(int i=1;i<=bin[n];i++)
            a[i]=read();
        dfs(1,0);
        cout<<ans<<endl;
        return 0;
    }
  • 相关阅读:
    进程间通信小结
    菜鸡和菜猫进行了一场Py交易
    菜鸡开始接触一些基本的算法逆向了
    菜鸡学逆向学得头皮发麻,终于它拿到了一段源代码
    静态分析-Windows找密码
    逆向-完成地址随机化关闭
    QSortFilterProxyModel 的过滤 排序
    linux命令2
    linux 命令1
    error c2059 c3905 c2148 c2238
  • 原文地址:https://www.cnblogs.com/Rorschach-XR/p/11164567.html
Copyright © 2020-2023  润新知