B(升降序列)
题目
【题目描述】
对于一个排列,考虑相邻的两个元素,如果后面一个比前面一个大,表示这个位置是上升的,用 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;
}