【BZOJ3990】【SDOI2015】排序

来源:互联网 发布:mac怎么关闭桌面 编辑:程序博客网 时间:2024/05/21 17:48

Description

小A有一个 12N 的排列 A[12N] ,他希望将 A 数组从小到大排序,小A可以执行的操作有 N 种,每种操作最多可以执行一次,对于所有的 i(1iN) ,第 i 中操作为将序列从左到右划分为 2Ni+1 段,每段恰好包括 2i1 个数,然后整体交换其中两段。小A想知道可以将数组 A 从小到大排序的不同的操作序列有多少个,小A认为两个操作序列不同,当且仅当操作个数不同,或者至少一个操作不同(种类不同或者操作位置不同)。

下面是一个操作事例:
N=3,A[18]=[3,6,1,2,7,8,5,4]
第一次操作,执行第 3 种操作,交换 A[14]A[58],交换后的 A[18][7,8,5,4,3,6,1,2]
第二次操作,执行第 1 种操作,交换 A[3]A[5] ,交换后的 A[18][7,8,3,4,5,6,1,2]
第三次操作,执行第 2 种操作,交换 A[12]A[78] ,交换后的 A[18][1,2,3,4,5,6,7,8]

Input

第一行,一个整数 N
第二行, 2N 个整数, A[12N]

Output

一个整数表示答案。

Sample Input

37 8 5 6 1 2 4 3

Sample Output

6

HINT

100% 的数据, 1N12

题解

这道题的想法真的很妙。
首先你需要发现一个神奇的性质,就是对于每一种可能的方案,每一种操作的顺序并不影响操作结果,影响操作结果的仅为选择操作种类的集合。所以对于每一种可行的操作集合 S ,它对答案的贡献为 |S|! 。转化为求所有可行的集合,由于 N 非常小, 2N 只有不到 104 的级别,所以完全可以直接爆搜出所有的解。
搜索的时候按照操作种类从小到大的顺序,因为只有这样才能在搜索比较大的区间时,小的区间已经有序。
假设当前搜索的操作种类为 t ,我们只需要判断对于所有无序的序列 [k×2t(k+1)×2t1] 的个数。如果个数 >2 ,那么无解;如果个数 =0, 那么不选择当前操作;如果个数 =1 ,那么当前操作调整这个无序的区间;如果个数 =2 ,那么当前操作调整这两个无序的区间。

My Code

#include <iostream>#include <cstring>#include <cmath>#include <algorithm>#include <cstdio>using namespace std;typedef long long ll;ll fac[15], ans;int n, N;int a[10005];int check(int pos, int k){    for(int i = 0; i < (1 << k); i ++){        if(a[pos + i] != a[pos] + i) return 0;    }    return 1;}void swp(int posx, int posy, int k){    for(int i = 0; i < (1 << k); i ++){        swap(a[posx + i], a[posy + i]);    }}void dfs(int t, int cur){    if(t == n){        ans += fac[cur]; return;    }    int m = 1 << t; int m2 = m + m;    int t1 = -1, t2 = -1;    for(int i = 0; i < N; i += m2){        if(!check(i, t + 1)){            if(t1 == -1) t1 = i;            else if(t2 == -1) t2 = i;            else return;        }    }    if(t1 == -1 && t2 == -1) dfs(t + 1, cur);    else if(t2 == -1){        swp(t1, t1 + m, t);        dfs(t + 1, cur + 1);        swp(t1, t1 + m, t);    }else{        for(int i = 0; i <= 1; i ++){            for(int j = 0; j <= 1; j ++){                swp(t1 + i * m, t2 + j * m, t);                if(check(t1, t + 1) && check(t2, t + 1)){                    dfs(t + 1, cur + 1);                    swp(t1 + i * m, t2 + j * m, t);                    break;                }                swp(t1 + i * m, t2 + j * m, t);            }        }    }}int main(){    scanf("%d", &n);    N = 1 << n;    for(int i = 0; i < N; i ++){        scanf("%d", &a[i]);    }    fac[0] = 1;    for(int i = 1; i <= n; i ++){        fac[i] = fac[i - 1] * ll(i);    }    dfs(0, 0);    printf("%lld\n", ans);    return 0;} /*33 6 1 2 7 8 5 46*/ 
原创粉丝点击