B(升降序列)

B(升降序列)

pic


题目

【题目描述】

对于一个排列,考虑相邻的两个元素,如果后面一个比前面一个大,表示这个位置是上升的,用 I 表示,反之这个位置是下降的,用 D 表示。如排列 3,1,2,7,4,6,5 可以表示为 DIIDID。

现在给出一个长度为 n-1 的排列表示,问有多少种 1 到 n 的排列满足这种表示。

【输入输出格式】

输入格式:
一个字符串 S,S 由 I,D,?组成。?表示这个位置既可以为 I,又可以为 D。

输出格式:
有多少种排列满足上述字符串。输出排列数模 1000000007。

【输入输出样例】

输入样例#1:
?D
输出样例#1:
3
【说明】
对于 20%的数据,S 长度 ≤ 10;
对于 100%的数据,S 长度 ≤ 1000。

思路

dp。
(dp[i][j])表示i这么长的序列,最后一个是j的符合要求的序列个数
于是有方程:

[dp_{i,j}= egin{cases} sum_{k=1}^{j-1} dp_{i-1,k} ext{Order='I'} \[1ex] \ sum_{k=j}^{i-1} dp_{i-1,k} ext{Order='D'}end{cases} ]

为了简化计算,计算(dp_{i,...})之前,把(dp_{i-1,...})处理成前缀和的形式。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define mod 1000000007
#define LL long long
#define File freopen("B.in","r",stdin);freopen("B.out","w",stdout)
using namespace std;
const int maxn=1000+5;

LL n=1,dp[maxn][maxn],ans;
char c;

int main() {
    ios::sync_with_stdio(false);
//    File;
    c=getchar();
    dp[1][1]=1;
    while(c=='?' || c=='D' || c=='I') {
        n++;
        for(LL i=1; i<n; ++i) {
            dp[n-1][i]=(dp[n-1][i]+dp[n-1][i-1])%mod;
        }
        if(c=='I') {
            for(LL i=2; i<=n; ++i)
                dp[n][i]=dp[n-1][i-1]%mod;
        }
        if(c=='D') {
            for(LL i=1; i<n; ++i) {
                dp[n][i]=(dp[n-1][n-1]-dp[n-1][i-1]+mod)%mod;
            }
        }
        if(c=='?') {
            for(LL i=2; i<=n; ++i) {
                dp[n][i]=dp[n-1][i-1]%mod;
            }
            for(LL i=1; i<n; ++i) {
                dp[n][i]+=(dp[n-1][n-1]-dp[n-1][i-1]+mod)%mod;
            }
        }
        c=getchar();
    }
    ans=0;
    for(LL i=1; i<=n; ++i) {
        ans+=dp[n][i];
        ans=ans%mod;
    }
    cout<<ans%mod;
    return 0;
}

原文地址:https://www.cnblogs.com/YQAccelerator/p/7413783.html