[BZOJ3990] [SDOI2015] 排序

A. 排序

题目描述

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

输入格式

第一行,一个整数$N$

第二行,$2^N$个整数,$A[1..2^N]$

输出格式

一行,一个整数,表示可以将数组$A$从小到大排序的不同的操作序列的个数。

样例

样例输入

3

7 8 5 6 1 2 4 3

样例输出

6

数据范围与提示

对于$30\%$的数据,$1<=N<=4$; 对于全部的数据,$1<=N<=12$。

 

题解

这个题考试的时候一开始想到了要用一颗树来做,然而并没有考虑全。

打了一个$dp$,结果最后发现树上的$dp$是错的,因为每层交换使之成立的情况有好几种。

考试错解($WA 15$)

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#define Reg register
using namespace std;
int n,jpp,jaa,jjj,anp[10050],pos[1<<13];
struct Tree {int fat,judge,num,ans;} tree[1000050];
void build(int k,int l,int r,int fat)
{
    tree[k].fat=fat;
    if(l==r)
    {
        scanf("%d",&tree[k].num);
        pos[tree[k].num]=k;
        return;
    }
    int mid=(l+r)/2;
    if(l<=mid) build(k*2,l,mid,k);
    if(mid+1<=r) build(k*2+1,mid+1,r,k);
    tree[k].num=max(tree[k*2].num,tree[k*2+1].num);
    return;
}
void dp(int k,int l,int r)
{
    if(l==r) return;
    int mid=(l+r)/2;
    if(l<=mid) dp(k*2,l,mid);
    if(mid+1<=r) dp(k*2+1,mid+1,r);
    if(r-l+1==2)
    {
        if(abs(tree[k*2].num-tree[k*2+1].num)==1)
        {
            if(tree[k*2].num>tree[k*2+1].num)
            {
                if(jaa) { jpp=1; return; }
                tree[k].ans=1;
            }
            return;
        }
        else
        {
            if(jaa==1) { jpp=1; return; }
            int ok=0;
            if(tree[k*2+1].num>tree[k*2].num)
            {
                ok=1;
                swap(tree[k*2+1].num,tree[k*2].num);
            }
            int p=pos[tree[k*2].num-1],judgg=0;
            if(tree[tree[p].fat*2].num==tree[k*2].num-1)   //右儿子
            {
                if(abs(tree[tree[p].fat*2+1].num-tree[k*2+1].num)==1)
                {
                    judgg=1;
                    swap(tree[tree[p].fat*2].num,tree[k*2+1].num);
                    tree[tree[p].fat].num=max(tree[tree[p].fat*2].num,tree[tree[p].fat*2+1].num);
                    tree[k].num=max(tree[k*2].num,tree[k*2+1].num);
                }
            }
            else
            {
                if(abs(tree[tree[p].fat*2].num-tree[k*2+1].num)==1)
                {
                    judgg=1;
                    swap(tree[tree[p].fat*2+1].num,tree[k*2+1].num);
                    tree[tree[p].fat].num=max(tree[tree[p].fat*2].num,tree[tree[p].fat*2+1].num);
                    tree[k].num=max(tree[k*2].num,tree[k*2+1].num);
                }
            }
            if(!judgg)
            {
                p=pos[tree[k*2+1].num+1];
                if(tree[tree[p].fat*2].num==tree[k*2+1].num+1)   //左儿子
                {
                    if(abs(tree[tree[p].fat*2+1].num-tree[k*2].num)==1)
                    {
                        judgg=1;
                        swap(tree[tree[p].fat*2].num,tree[k*2].num);
                        tree[tree[p].fat].num=max(tree[tree[p].fat*2].num,tree[tree[p].fat*2+1].num);
                        tree[k].num=max(tree[k*2].num,tree[k*2+1].num);
                    }
                }
                else
                {
                    if(abs(tree[tree[p].fat*2].num-tree[k*2].num)==1)
                    {
                        judgg=1;
                        swap(tree[tree[p].fat*2+1].num,tree[k*2].num);
                        tree[tree[p].fat].num=max(tree[tree[p].fat*2].num,tree[tree[p].fat*2+1].num);
                        tree[k].num=max(tree[k*2].num,tree[k*2+1].num);
                    }
                }
            }
            if(!judgg) jpp=1;
            if(ok) swap(tree[k*2+1].num,tree[k*2].num);
            if(judgg)
            {
                jaa=1;
                if(tree[k*2].num>tree[k*2+1].num)
                {
                    if(jaa) { jpp=1; return; }
                    tree[k].ans=1;
                }
                return;
            }
        }
    }
    else
    {
        if(tree[k*2].num<tree[k*2+1].num)
        {
            if(tree[k*2].judge&&tree[k*2+1].judge) jpp=1;
            else tree[k].ans=tree[k*2].ans+tree[k*2+1].ans;
        }
        else
        {
            if(tree[k*2].judge&&tree[k*2+1].judge) jpp=1;
            else
            {
                tree[k].ans=tree[k*2].ans+tree[k*2+1].ans+1;
                tree[k].judge=1;
            }
        }
    }
    return;
}
void cheng(int x)
{
    int y=0;
    for(Reg int i=1;i<=anp[0];++i)
    {
        anp[i]*=x;
        anp[i]+=y;
        y=anp[i]/10;
        anp[i]%=10;
    }
    while(y)
    {
        anp[++anp[0]]=y%10;
        y/=10;
    }
    return;
}
void dfs(int k,int l,int r)
{
    if(l==r) cout<<tree[k].num<<' ';
    else
    {
        int mid=(l+r)/2;
        if(l<=mid) dfs(k*2,l,mid);
        if(mid+1<=r) dfs(k*2+1,mid+1,r);
    }
    return;
}
int main()
{
    scanf("%d",&n);
    build(1,1,1<<n,0);
    dp(1,1,1<<n);
    if(jpp) printf("0");
    else
    {
        anp[0]=1,anp[1]=1;
        for(Reg int i=2;i<=tree[1].ans+jaa;++i) cheng(i);
        for(Reg int i=anp[0];i>=1;--i) printf("%d",anp[i]);
    }
    return 0;
}
View Code

考试暴力($TLE 30$)

#include<iostream>
#include<cstring>
#include<cstdio>
#define Reg register
using namespace std;
int ans,n,num[(1<<12)+1];
void dfs(int state)
{
    bool k=1;
    for(Reg int i=1;i<=(1<<n);++i)
        if(num[i]!=i) { k=0; break; }
    if(k==1) ++ans;
    else
    {
        for(Reg int i=1;i<=n;++i)
        {
            if((state&(1<<(i-1)))) continue;
            for(Reg int j=1;j<=(1<<i);++j)
            {
                for(Reg int k=j+1;k<=(1<<i);++k)
                {
                    for(Reg int p=1;p<=(1<<(n-i));++p)
                    {
                        swap(num[(1<<(n-i))*(k-1)+p],num[(1<<(n-i))*(j-1)+p]);
                    }
                    dfs((state|(1<<(i-1))));
                    for(Reg int p=1;p<=(1<<(n-i));++p)
                    {
                        swap(num[(1<<(n-i))*(k-1)+p],num[(1<<(n-i))*(j-1)+p]);
                    }
                }
            }
        }
    }
    return;
}
int main()
{
    scanf("%d",&n);
    for(Reg int i=1;i<=(1<<n);++i) scanf("%d",&num[i]);
    dfs(0);
    printf("%d",ans);
    return 0;
}
/*
11 2 3 7 4 10 1 13 5 9 14 16 6 15 8 12
5 6 7 8 1 3 2 4
*/
View Code

正解($AC$)

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<vector>
#define Reg register
using namespace std;
int vis[100050],vpp[(1<<12)+10];
int n,tot,num[(1<<12)+10],st[150],ed[150];
struct Tree {int lch,rch,fat,num,dep;} tree[1000050];
long long app;
void build(int k,int l,int r,int deep,int fat)
{
    tree[k].fat=fat;
    tree[k].dep=deep;
    st[deep]=min(st[deep],k);
    ed[deep]=max(ed[deep],k);
    if(l==r)
    {
        scanf("%d",&tree[k].num);
        return;
    }
    int mid=(l+r)/2;
    if(l<=mid)
    {
        tree[k].lch=k*2;
        build(k*2,l,mid,deep+1,k);
    }
    if(mid+1<=r)
    {
        tree[k].rch=k*2+1;
        build(k*2+1,mid+1,r,deep+1,k);
    }
    return;
}
bool judge(int x,int deep)
{
    if(abs(tree[tree[x].lch].num-tree[tree[x].rch].num)>(1<<(n-deep-1))) return 0;
    if(tree[tree[x].lch].num>tree[tree[x].rch].num) return 0;
    return 1;
}
void update(int deep)
{
    for(Reg int i=st[deep];i<=ed[deep];++i)
        tree[i].num=max(tree[tree[i].lch].num,tree[tree[i].rch].num);
    return;
}
void swap(int x,int y)
{
    int m=tree[y].num,n=tree[x].num;
    tree[x].num=m,tree[y].num=n;
    return;
}
void dfs(int ans,int deep,int state)
{
    if(deep==-1)
    {
        if(vpp[state]) return;
        vpp[state]=1;
        long long lss=1;
        for(Reg int i=2;i<=ans;++i) lss*=i;
        app+=lss;
        return;
    }
    vis[0]=0;
    for(Reg int i=st[deep];i<=ed[deep];++i)
    {
        if(abs(tree[tree[i].lch].num-tree[tree[i].rch].num)>(1<<(n-deep-1))) vis[++vis[0]]=i;
        else if(tree[tree[i].lch].num>tree[tree[i].rch].num) vis[++vis[0]]=i;
    }
    if(vis[0]>=3) return;
    else if(vis[0]==2)
    {
        int p1=vis[1],p2=vis[2];
        
        swap(tree[p1].lch,tree[p2].lch);
        if(judge(p1,deep)&&judge(p2,deep))
        {
            update(deep);
            dfs(ans+1,deep-1,state|(1<<deep));
        }
        swap(tree[p1].lch,tree[p2].lch);          //左儿子换左儿子
        update(deep);
        
        swap(tree[p1].rch,tree[p2].lch);
        if(judge(p1,deep)&&judge(p2,deep))
        {
            update(deep);
            dfs(ans+1,deep-1,state|(1<<deep));
        }
        swap(tree[p1].rch,tree[p2].lch);          //右儿子换左儿子
        update(deep);
        
        swap(tree[p1].lch,tree[p2].rch);
        if(judge(p1,deep)&&judge(p2,deep))
        {
            update(deep);
            dfs(ans+1,deep-1,state|(1<<deep));
        }
        swap(tree[p1].lch,tree[p2].rch);          //左儿子换右儿子
        update(deep);
        
        swap(tree[p1].rch,tree[p2].rch);
        if(judge(p1,deep)&&judge(p2,deep))
        {
            update(deep);
            dfs(ans+1,deep-1,state|(1<<deep));
        }
        swap(tree[p1].rch,tree[p2].rch);          //右儿子换右儿子
        update(deep);
    }
    else if(vis[0]==1)
    {
        int p=vis[1];
        if(abs(tree[tree[p].lch].num-tree[tree[p].rch].num)>(1<<(n-deep))) return;
        swap(tree[p].lch,tree[p].rch);
        update(deep);
        dfs(ans+1,deep-1,state|(1<<deep));
        swap(tree[p].lch,tree[p].rch);
        update(deep);
    }
    else if(vis[0]==0)
    {
        update(deep);
        dfs(ans,deep-1,state);
    }
    return;
}
int main()
{
    scanf("%d",&n);
    memset(st,0x7f,sizeof(st));
    memset(ed,0,sizeof(ed));
    build(1,1,1<<n,0,0);
    dfs(0,n-1,0);
    printf("%lld",app);
    return 0;
}
View Code

其实这个题思路很简单,因为总共有2的整数次幂倍,所以很容易想到完全二叉树,

如果要使最后的序列合法,那么肯定每一层都要合法。

 

看样例: $7 8 5 6 1 2 4 3$

我们发现假如两个数一组,那么会分成${7 8}$,${5 6}$,${1 2}$,${4 3}$。

那么肯定要让$4$、$3$互换,${7 8}$,${5 6}$,${1 2}$,${3 4}$。

再让4个数一组,${{7 8} {5 6}},{{1 2} {3 4}}$,肯定要让${7 8}$、${5 6}$互换。

变成$ {{5 6} {7 8}}$,${{1 2} {3 4}}$。

最后肯定在让最后两个区间互换。

 

如果我们找到一种合法的方案,那么换的顺序可以随意,也就是方案数为它的阶乘。

然后考虑每一层,

可以分4种情况:

1、不合法的数的组数有$>2$个,那么不可能通过交换$1$次使其合法。

2、不合法的数的组数有$2$个,那么我们让他们两两交换,也就是 左儿子交换右儿子、左儿子交换左儿子......看它合不合法。

   这可能会有好几种合法的情况,比如:$1 4 3 2$

   既可以变成$ 1 2 3 4$,也可以变成$3 4 1 2$。

3、不合法的数的组数有$1$个,那么考虑,

   一个例子:$ 1 4 2 3$,那么这种情况不可能一次交换合法。

   所以要判断一下这两个数的差。

4、没有不合法的数,那么就不需交换。

这里的不合法有两种情况,一种是左儿子大于右儿子,另一种是两儿子的差大于$1<<(n-deep-1)$。

我们要上传区间最大值。

具体实现看代码。

posted @ 2019-07-15 08:16  Milk_Feng  阅读(142)  评论(0编辑  收藏  举报