• [SDOI2015][BZOJ3990] 排序


    3990: [SDOI2015]排序

    Time Limit: 20 Sec  Memory Limit: 128 MB
    Submit: 175  Solved: 94
    [Submit][Status][Discuss]

    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.

     
    考场上直接暴力35分……
    分析:
    定理1:
    对于每种合法的操作序列,其序列顺序是无关的,所以求出一种合法的序列,求出他的全排列(即阶乘)就ok了,效率++;
    因此我们可以从小到大操作。
    定理2:
    假如序列为 [3 6 1 2 7 8 5 4] ,我们要进行长度为1的段操作,那么长度为2的段中不满足递增的段的个数num,若num>2则无解,num=2,有四种交换方式,num=1,有一种交换方式,num=0,不用交换,继续递归。
     
    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<cmath>
    #include<vector>
    using namespace std;
    int n,a[4100],sum;
    long long ans,jc[13];
    void change(int l,int r,int val)
    {
        for (int i=0;i<val;i++)
        {
            int t=a[l+i];
            a[l+i]=a[r+i];
            a[r+i]=t;
        }
    }
    void dfs(int x,int k)
    {
        if (x==n)
        {
            ans+=jc[k];
            return;
        }
        int tot=0; 
        int b[3]={0,0,0};
        int val=1<<x;
        for (int i=1;i<=sum;i+=(val<<1))
            if (a[i+val-1]+1!=a[i+val])
            { 
                if (tot==2) return; 
                b[++tot]=i;
            }        
        if (tot==2)
        {
            if (a[b[1]+val-1]+1==a[b[2]+val]&&a[b[1]+val]-1==a[b[2]+val-1])
            {
                change(b[1],b[2],val);
                dfs(x+1,k+1);
                change(b[1],b[2],val);
                change(b[1]+val,b[2]+val,val);
                dfs(x+1,k+1);
                change(b[1]+val,b[2]+val,val);
            }
            if (a[b[1]+val]-1==a[b[2]+val*2-1]&&a[b[2]+val-1]+1==a[b[1]])
            {
                change(b[1],b[2]+val,val);
                dfs(x+1,k+1);
                change(b[1],b[2]+val,val);
            }
            if (a[b[1]+val-1]+1==a[b[2]]&&a[b[1]+val*2-1]==a[b[2]+val]-1)
            {
                change(b[1]+val,b[2],val);
                dfs(x+1,k+1);
                change(b[1]+val,b[2],val);
            }
            return; 
        }
        if (tot==1)
        {
            change(b[1],b[1]+val,val);
            dfs(x+1,k+1);
            change(b[1],b[1]+val,val);
            return;
        }
        if (tot==0) 
        {
            dfs(x+1,k);
            return;
        }
    }
    int main()
    {
        scanf("%d",&n);    
        sum=1<<n;
        for (int i=1;i<=sum;i++) scanf("%d",&a[i]);
        jc[0]=1;
        for (int i=1;i<=n;i++) jc[i]=jc[i-1]*i;
        ans=0;
        dfs(0,0);
        printf("%d",ans);
        return 0;
    }
  • 相关阅读:
    cf914D. Bash and a Tough Math Puzzle(线段树)
    RNQOJ [stupid]愚蠢的矿工(树形依赖背包)
    BZOJ4552: [Tjoi2016&Heoi2016]排序(线段树 二分)
    多项式系数学习笔记
    BZOJ4653: [Noi2016]区间(线段树 双指针)
    洛谷P3372 【模板】线段树 1(树状数组)
    BZOJ3261: 最大异或和(可持久化trie树)
    BZOJ4260: Codechef REBXOR (01Tire树)
    Android 关于显示键盘,布局错乱网上顶的问题
    Java 输入流读取文本文件换行符问题
  • 原文地址:https://www.cnblogs.com/ws-fqk/p/4503643.html
Copyright © 2020-2023  润新知