传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=3990

Solution

首先可以发现如果找到一种方案S可行,则S的全排列也可行。因此可以从小到大选每段长度,
每次找不是连续上升的段:
如果没有这样的段,则无需操作
若有一段,则交换该段的前半部分和后半部分判断,继续往下搜
若有两段,则分四种交换情况讨论,和一段的情况类似
如果这样的段大于两段,则显然不可行;
每次找到可行解则累加阶乘。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 20
#define ll int
using namespace std;
inline ll read(){
    char ch; ch=getchar(); ll x=0,f=1;
    while (ch<'0'||ch>'9'){if (ch=='-') f=-1; ch=getchar();}
    while (ch>='0'&&ch<='9'){x=x*10+ch-'0'; ch=getchar();}
    return x*f;
}
inline void write_(ll x){
    if (x<0) x=-x,putchar('-');
    if (x>9) write_(x/10); putchar(x%10+'0');
}
inline void write(ll x){write_(x); putchar(' ');}
inline void writeln(ll x){write_(x); putchar('\n');}
ll a[5000],n,fac[N],bin[N],ans;
ll check(ll x,ll k){
    for (ll i=1;i<bin[k];i++)
        if (a[x+i]!=a[x+i-1]+1) return 0;
    return 1;
}
void Swap(ll x,ll y,ll k){
    for (ll i=0;i<k;i++)
        swap(a[x+i],a[y+i]);
}
void dfs(ll k,ll now){
    if (k==n+1){
        ans+=fac[now];
        return;
    }
    ll t1=0,t2=0;
    for (ll i=1;i<=bin[n];i+=bin[k])
        if (!check(i,k)){
            if (!t1) t1=i;
            else if (!t2) t2=i;
            else return;
        }
    if (!t1&&!t2) dfs(k+1,now);
    else if (!t2){
        Swap(t1,t1+bin[k-1],bin[k-1]);
        dfs(k+1,now+1);
        Swap(t1,t1+bin[k-1],bin[k-1]);
    }else{
        for (ll x=0;x<=1;x++)
        for (ll y=0;y<=1;y++){
            Swap(t1+x*bin[k-1],t2+y*bin[k-1],bin[k-1]);
            if (check(t1,k)&&check(t2,k)){
                dfs(k+1,now+1);
                Swap(t1+x*bin[k-1],t2+y*bin[k-1],bin[k-1]);
                break;
            }
            Swap(t1+x*bin[k-1],t2+y*bin[k-1],bin[k-1]);
        }
    }
}
int main(){
    fac[0]=bin[0]=1; n=read();
    for (ll i=1;i<=14;i++) fac[i]=fac[i-1]*i;
    for (ll i=1;i<=14;i++) bin[i]=bin[i-1]<<1;
    for (ll i=1;i<=bin[n];i++) a[i]=read();
    dfs(1,0);
    writeln(ans);
}
文章目录
  1. 1. Solution