zoukankan      html  css  js  c++  java
  • 1768:交换

    【题目描述】

    给定一个{0,1,2,3,…,n-1}的排列 p。

    一个{0,1,2,…,n-2}的排列q被认为是优美的排列,当且仅当q

    满足下列条件:

    对排列s={0,1,2,3,...,n-1}进行n–1次交换。

    ①交换s[q0],s[q0+1]。

    ②交换s[q1],s[q1+1]。

    ……

    最后能使得排列s=p。

    问有多少个优美的排列,答案对1e9+7取模。

    【输入】

    第一行一个正整数n。

    第二行n个整数代表排列p。
     
    【输出】

    仅一行表示答案。

    【输入样例】

    3
    1 2 0

    【输出样例】

    1

    【样例解释】

    q={0,1}{0,1,2}→{1,0,2}→{1,2,0}

    q={1,0}{0,1,2}→{0,2,1}→{2,0,1}

    【数据规模】

    对于30%的数据,n≤10。

    对于100%的数据,n≤50。

     

    【题解】

     

    可以考虑变为将p数组变为{0,1,……,n-1}的q数组有好多个。

     

    令f[i][j]表示把原序列中的i个数{j,j+1,……,j+i-1}排列成升序的方案数。则答案为f[n][0]。

     

    转移:

     

    发现每个i和i+1只能交换一次,所以换了后两边两个区间相对独立。

     

    枚举断点,在i右边k-1个,若交换后i-k段内数为i-k且k-j段内数为k-j则可以交换,f[i][n]=inom{i-2}{k-1}*f[k][n]*f[i-k][n+k]。(乘以inom{i-2}{k-1}是选k-1次在左边交换)。

     

    可以预处理出对于[i,j]段内所有数的相对位置,例如数列p 3 0 1 2 的yc[1][3]即为{3,1,2}。这样就可以判断交换后是否合法。(因为设此时区间为i,j如果交换在同在左区间两个数(即改变相对位置)则左右区间内的数肯定不合法,则预处理出相对位置可行)

     

    代码如下:

    #include<bits/stdc++.h>
    #define int long long
    using namespace std;
    const int mo=1e9+7;
    const int N=55;
    int C[N][N],f[N][N],n,p[N],xi[N][N][N];//xi表示[i,j]的相对位置排列 
    inline void yclxi()
    {
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
            {
                int tot=0;
                for(int k=1;k<=n;k++)
                    if(p[k]>=i&&p[k]<=j)
                        xi[i][j][i+(tot++)]=p[k];
            }
        for(int i=0;i<=n;i++) C[i][0]=1;
        for(int i=1;i<=n;i++)
            for(int j=1;j<=i;j++)
                C[i][j]=(C[i-1][j]+C[i-1][j-1])%mo;
    }
    inline bool check(int l,int r,int mid)
    {
        for(int i=l;i<=mid-1;i++) if(xi[l][r][i]>mid) return 0;
        if(xi[l][r][mid+1]>mid) return 0;
        for(int i=mid+2;i<=r;i++) if(xi[l][r][i]<=mid) return 0;
        if(xi[l][r][mid]<=mid) return 0;
        return 1;
    }
    inline int dp(int go,int co)//dp[i][j]表示有i个数:(从j到j+i-1) 排成升序的方案数。
    {
        if(f[go][co]) return f[go][co];
        if(go==1)
        {
            f[go][co]=1;
            return 1;
        }
        int daan=0;
        for(int i=1;i<=go-1;i++)
        {
            if(check(co,co+go-1,co+i-1))
                daan+=C[go-2][i-1]*dp(i,co)%mo*dp(go-i,co+i)%mo,daan%=mo;
        }
        f[go][co]=daan;
        return daan;
    }
    signed main()
    {
        cin>>n;
        for(int i=1;i<=n;i++) cin>>p[i],p[i]++;
        yclxi();
        cout<<dp(n,1);
        return 0;
    }
    View Code
  • 相关阅读:
    2020.8.20收获
    2020.8.19
    2020.8.21收获
    2020.8.24收获
    UIScrollView滑动动作结束的侦听函数
    iphone 自定义UISwitch
    总结SQLite不支持的SQL语法有哪些
    去除nsstring中的空格
    ObjectiveC中判断字符串是否包含其他字符串
    设置IPHONE顶部的状态栏的样式
  • 原文地址:https://www.cnblogs.com/betablewaloot/p/12194139.html
Copyright © 2011-2022 走看看