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]=\(binom{i-2}{k-1}\)*f[k][n]*f[i-k][n+k]。(乘以\(binom{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
posted @ 2020-01-14 21:18  精海臭脚  阅读(379)  评论(0编辑  收藏  举报