• [SDOI2015][bzoj3990] 序列 [搜索]


    题面

    传送门

    思路

    首先,这道题目有一个非常显然(但是我不会严格证明,只能意会一下)的结论:一个合法的操作序列中,任意两个操作是可以互换的

    那么,这个结论加上本题极小的数据范围,为什么不搜索一下呢?

    ok说干就干

    既然顺序不重要,我们就从交换两个长度为1的序列开始搜索

    这时,就有另外一个性质:因为我们每种交换能且只能做一次,所以有的情况下我们是一定无法完成的

    考虑交换两个长度为$2k$的序列,这时我们把整个序列分成长度为$2{k+1}$的段

    如果这些段全部都是连续且递增(也就是3,4,5,6这样)的,说明这个操作不用做

    如果这些段里面有一个不是连续递增的,就把这个段的前后两半交换

    如果这些段里面有两个不是连续递增的,那么我们对于这两段的两半,讨论四种交换的情况,分别判断它们是否合法

    如果这些段里面有超过两个不是连续递增的,那么可以证明此时我们一定无法完成排序,可以把这个搜索枝剪掉

    这样操作以后,我们会发现,对于所有合法的长度为$2k$的序列的交换,完成之后的序列,一定由若干个长度为$2{k+1}$的连续递增序列构成

    这时我们再递归到下一层处理,递归n层以后要判断一下最终序列是否是1-n,然后用当前这个操作序列中的操作个数的阶乘加到答案上(因为可以随意改变操作顺序)

    总时间复杂度为$O(2^{24})$,但是远远达不到这个值

    Code

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define ll long long
    #include<vector>
    using namespace std;
    inline ll read(){
        ll re=0,flag=1;char ch=getchar();
        while(ch>'9'||ch<'0'){
            if(ch=='-') flag=-1;
            ch=getchar();
        }
        while(ch>='0'&&ch<='9') re=(re<<1)+(re<<3)+ch-'0',ch=getchar();
        return re*flag;
    } 
    ll n,a[5010],tmp[5010][15];
    vector<int>ans;//不同的操作序列的操作个数
    void dfs(ll k,ll num){
        ll i,j,t,cnt=0,m1,m2;//cnt是非连续递增的段的个数,m1m2是前两个的起点
        if(k==n+1){
            for(i=1;i<=(1<<n);i++) if(tmp[i][k]!=i) return;
            ans.push_back(num);return;//注意到
        }
        for(i=1;i<=(1<<n);i+=(1<<k)){//统计cnt
            t=tmp[i][k];
            for(j=i+1;j<i+(1<<k);j++){
                if(tmp[j][k]!=t+j-i){
                    if(cnt==2) return;
                    cnt++;
                    if(cnt==1) m1=i;
                    else m2=i;
                    break;
                }
            }
        }
        if(cnt>2) return;
        for(i=1;i<=(1<<n);i++) tmp[i][k+1]=tmp[i][k];
        if(cnt==0){
            dfs(k+1,num);return;
        }
        if(cnt==1){
            for(j=m1;j<m1+(1<<k-1);j++) swap(tmp[j][k+1],tmp[j+(1<<k-1)][k+1]);
            dfs(k+1,num+1);return;
        }
        bool flag=1;//这里开始枚举四种情况
        for(i=1;i<=(1<<k-1);i++) swap(tmp[m1+i-1][k+1],tmp[m2+i-1][k+1]);
        for(i=1;i<=(1<<k);i++) if(tmp[m1+i-1][k+1]-tmp[m1][k+1]!=i-1) flag=0;
        for(i=1;i<=(1<<k);i++) if(tmp[m2+i-1][k+1]-tmp[m2][k+1]!=i-1) flag=0;
        if(flag) dfs(k+1,num+1);
        flag=1;
        for(i=1;i<=(1<<n);i++) tmp[i][k+1]=tmp[i][k];
        for(i=1;i<=(1<<k-1);i++) swap(tmp[m1+(1<<k-1)+i-1][k+1],tmp[m2+i-1][k+1]);
        for(i=1;i<=(1<<k);i++) if(tmp[m1+i-1][k+1]-tmp[m1][k+1]!=i-1) flag=0;
        for(i=1;i<=(1<<k);i++) if(tmp[m2+i-1][k+1]-tmp[m2][k+1]!=i-1) flag=0;
        if(flag) dfs(k+1,num+1);
        flag=1;
        for(i=1;i<=(1<<n);i++) tmp[i][k+1]=tmp[i][k];
        for(i=1;i<=(1<<k-1);i++) swap(tmp[m1+i-1][k+1],tmp[m2+(1<<k-1)+i-1][k+1]);
        for(i=1;i<=(1<<k);i++) if(tmp[m1+i-1][k+1]-tmp[m1][k+1]!=i-1) flag=0;
        for(i=1;i<=(1<<k);i++) if(tmp[m2+i-1][k+1]-tmp[m2][k+1]!=i-1) flag=0;
        if(flag) dfs(k+1,num+1);
        flag=1;
        for(i=1;i<=(1<<n);i++) tmp[i][k+1]=tmp[i][k];
        for(i=1;i<=(1<<k-1);i++) swap(tmp[m1+(1<<k-1)+i-1][k+1],tmp[m2+(1<<k-1)+i-1][k+1]);
        for(i=1;i<=(1<<k);i++) if(tmp[m1+i-1][k+1]-tmp[m1][k+1]!=i-1) flag=0;
        for(i=1;i<=(1<<k);i++) if(tmp[m2+i-1][k+1]-tmp[m2][k+1]!=i-1) flag=0;
        if(flag) dfs(k+1,num+1);
    }
    int main(){
        n=read();ll i,tans=1,out=0,j;
        for(i=1;i<=(1<<n);i++) a[i]=read(),tmp[i][1]=a[i];
        dfs(1,0);
        for(i=0;i<ans.size();i++){//阶乘更新答案
            tans=1;
            for(j=1;j<=ans[i];j++) tans*=j;
            out+=tans;
        }
        cout<<out;
    }
    
  • 相关阅读:
    web.xml+spring mvc基本配置
    REST服务安全-双向认证
    thymeleaf 配置
    jenkins
    linux下ssh/scp无密钥登陆方法
    java编译 Error: Could not find or load main class java执行包main方法
    文本按列导入excel
    linux脚本-判断进程是否存在,从而可以做预警处理..
    Linux中顿号
    >/dev/null 2>&1
  • 原文地址:https://www.cnblogs.com/dedicatus545/p/9255850.html
Copyright © 2020-2023  润新知