您的位置:首页 > 其它

BZOJ3990:[SDOI2015]排序——题解

2018-06-20 09:48 260 查看

https://www.lydsy.com/JudgeOnline/problem.php?id=3990

小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].

考虑暴力怎么写……不会啊。

我们能知道实际上操作顺序不影响结果,于是我们大可以从1~n枚举操作做。

因为最终变得有序,所以我们可以对于每一“大块”查询是否已经排好序了,如果没排好,就说明这一“大块”里的两小块肯定是放错位置的。

如果这样的小块<=4的话我们还好办,显然是前一“大块”的其中一“小块”和后一“大块”的其中一“小块”交换位置。

如果<=2那么就这两块交换就行了。

如果>4我们就处理不了了。

#include<map>
#include<cmath>
#include<stack>
#include<queue>
#include<cstdio>
#include<cctype>
#include<vector>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=1<<13;
inline int read(){
int X=0,w=0;char ch=0;
while(!isdigit(ch)){w|=ch=='-';ch=getchar();}
while(isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
return w?-X:X;
}
int n,m,a
;
ll jc
,ans;
bool pan(int l,int r){
for(int i=l;i<r;i++)
if(a[i]+1!=a[i+1])return 0;
return 1;
}
void mdy(int l,int r,int k){
for(int i=l,j=r;i<l+(1<<k);i++,j++)
swap(a[i],a[j]);
}
void dfs(int l,int num){
if(l>n){
ans+=jc[num];
return;
}
int tmp[4],tot=0;
for(int i=1;i<=m;i+=(1<<l))
if(!pan(i,i+(1<<l)-1)){
tmp[++tot]=i;
if(tot>2)return;
}
if(!tot){dfs(l+1,num);return;}
else if(tot==1){
mdy(tmp[1],tmp[1]+(1<<l-1),l-1);
if(pan(tmp[1],tmp[1]+(1<<l-1)))dfs(l+1,num+1);
mdy(tmp[1],tmp[1]+(1<<l-1),l-1);
}else{
for(int i=0;i<=1;i++)
for(int j=0;j<=1;j++){
mdy(tmp[1]+(i<<(l-1)),tmp[2]+(j<<(l-1)),l-1);
if(pan(tmp[1],tmp[1]+(1<<l-1))&&
pan(tmp[2],tmp[2]+(1<<l-1)))dfs(l+1,num+1);
mdy(tmp[1]+(i<<(l-1)),tmp[2]+(j<<(l-1)),l-1);
}
}
}
int main(){
n=read(),m=1<<n;
jc[0]=1;for(int i=1;i<=n;i++)jc[i]=jc[i-1]*i;
for(int i=1;i<=m;i++)a[i]=read();
dfs(1,0);
printf("%lld\n",ans);
return 0;
}

+++++++++++++++++++++++++++++++++++++++++++

+本文作者:luyouqi233。               +

+欢迎访问我的博客:http://www.cnblogs.com/luyouqi233/+

+++++++++++++++++++++++++++++++++++++++++++

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: