BZOJ 3990: [SDOI2015]排序(搜索+剪枝)

                                                      [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

HINT

100%的数据, 1<=N<=12.

Source

Round 1 感谢ZKY制作非官方数据

 

分析:

显然,对于每一种方案,我改变其操作顺序,仍然不影响结果,所以我们只需枚举每种操作做与不做即可,然后将排列数加入答案即可。

考虑进一步优化,对于第i种操作,将序列分为若干个长为2^(i-1)的小段,由于每种操作只能用一次,则当每个小段各自元素都连续且递增,就不需要这种操作了(相当于每2^(i-1)个为一组,每组都排好了序,要想是整体进一步排序,只能依靠下一步操作来排了),如果有一个不连续递增,则将这组分为两份后交换即可,如果有2组不满足,分四种情况讨论,最后如果超过2个,显然无法使它们有序,则对这种方案剪枝。

关键思想是由局部有序一直操作直至全局有序,当枚举第i种操作时,此时2^(i-2)长度的段必然是有序的(不合法情况已去除),只需考虑2^(i-1)长度的段即可。

program sort;
var
  a:array[0..10000]of longint;
  n,i,m:longint; ans,tot:int64;
procedure swap(x1,y1,x2,y2:longint);
var i,t:longint;
begin
  for i:=0 to y1-x1 do
   begin t:=a[x1+i]; a[x1+i]:=a[x2+i]; a[x2+i]:=t; end;
end;
function cheak:boolean;
var i:longint;
begin
  for i:=2 to m do if a[i]<>a[i-1]+1 then exit(false);
  exit(true);
end;
procedure dfs(x:longint);
var i,j,t,v,s,k:longint; l:int64;   w:array[0..2]of longint;
begin
  if x>=n then
   begin if cheak=false then exit;l:=1;for i:=1 to tot do l:=l*i; inc(ans,l); exit; end;
   t:=1 shl (x+1); v:=1 shl x; s:=v; k:=0;
   for i:=1 to m div t do
   begin
    if (a[s]+1<>a[s+1])or(a[s] mod v<>0) then begin inc(k); if k>2 then exit;w[k]:=s;  end;
    s:=s+t;
   end;
   if k=0 then dfs(x+1);
   inc(tot);
   if k=1 then begin swap(w[1]-v+1,w[1],w[1]+1,w[1]+v); dfs(x+1); swap(w[1]-v+1,w[1],w[1]+1,w[1]+v); end;
   if k=2 then
    begin
     if (a[w[1]]+1=a[w[2]+1])and(a[w[2]]+1=a[w[1]+1]) then
      begin
       swap(w[1]-v+1,w[1],w[2]-v+1,w[2]); dfs(x+1);  swap(w[1]-v+1,w[1],w[2]-v+1,w[2]);
       swap(w[1]+1,w[1]+v,w[2]+1,w[2]+v); dfs(x+1);  swap(w[1]+1,w[1]+v,w[2]+1,w[2]+v);
      end;
     if (a[w[2]]+1=a[w[1]-v+1])and(a[w[2]+v]+1=a[w[1]+1]) then
      begin swap(w[1]-v+1,w[1],w[2]+1,w[2]+v);dfs(x+1); swap(w[1]-v+1,w[1],w[2]+1,w[2]+v);end;
     if (a[w[1]+v]+1=a[w[2]+1])and(a[w[1]]+1=a[w[2]-v+1]) then
      begin swap(w[1]+1,w[1]+v,w[2]-v+1,w[2]);dfs(x+1); swap(w[1]+1,w[1]+v,w[2]-v+1,w[2]); end;
    end;
   dec(tot);
end;
begin
  readln(n);m:=1 shl n;
  for i:=1 to m do read(a[i]);
  ans:=0; tot:=0;
  dfs(0);
  writeln(ans);
end.
View Code

 

posted @ 2016-09-24 13:54  QTY_YTQ  阅读(223)  评论(0编辑  收藏  举报