链接
https://codeforces.com/problemset/problem/909/C
描述
这个题是吐槽python用tab做域界定符的。。
给你一个n行的python程序,每行要么是 for 语句, 要么是普通语句
其中for语句不允许空循环体
问你有多少种方式加tab界定域,使代码执行路径不同
(保证给定程序有效)
分析
首先我们可以看出一个普通语句前方紧挨着的一堆for 与 这个普通语句 是一个整体。
也就是说 “ffffffffffffs” 这个结构,在每个合法方案中的表达形式都是一致的,所以我们先将输入做一个转化,转化为 记录每个s前有多少个f的形式。
例:
fffs
s
ffs
转化为
3
0
2
然后再来看看每一个fs整体连接到已存在的一堆代码时都干了什么
- 选择一系列 已存在的一堆代码 的排列方式,它们的共性是最后的语句的缩进层数是deep层
- 向前缩进i个单位(0 <= i <= deep)
- __把整体中的第一个f连到它上面(缩进层数为deep-i),产生新的连接方式
- __选择新的i,回到2,直到遍历0到deep所有的i
- 选择新的系列(新的deep),回到1,直到遍历所有的系列
知道这些我们就可以开始dp了,设dp[i][deep] = 添加前i个fs整体后 ,产生的新结构中最后一的语句的缩进层数是deep层的种类数
那么初始状态就只有dp[0][0] = 1,其余都是0。
接着来看如何转移
从上面那组数字开始看起,我们的dp数组起始如下
deep | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
---|---|---|---|---|---|---|---|---|---|---|---|
dp[0][deep] | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
表示一个空行只有结尾缩进为0一种答案
那么我们接上第一个长度为3的fs结构时,我们要进行以下操作
- 选择dp[0]层中非0元素dp[0][0] = 1
- 缩进0到0个单位
- 更新答案
dp[1][0+3] += dp[0][0] = 1
deep | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
---|---|---|---|---|---|---|---|---|---|---|---|
dp[1][deep] | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
接第二个长度为0的结构时,操作如下
- 选择dp[1]层中非0元素dp[1][3] = 1
- 缩进0到3个单位
- 更新答案
dp[2][0+0] += dp[1][3] = 1
dp[2][1+0] += dp[1][3] = 1
dp[2][2+0] += dp[1][3] = 1
dp[2][3+0] += dp[1][3] = 1
deep | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
---|---|---|---|---|---|---|---|---|---|---|---|
dp[2][deep] | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
接第三个长度为2的结构时,操作如下
- 选择dp[2]层中非0元素dp[2][3] = 1
- 缩进0到3个单位
- 更新答案
dp[3][0+2] += dp[2][3] = 1
dp[3][1+2] += dp[2][3] = 1
dp[3][2+2] += dp[2][3] = 1
dp[3][3+2] += dp[2][3] = 1 - 选择dp[2]层中非0元素dp[2][2] = 1
- 缩进0到2个单位
- 更新答案
dp[3][0+2] += dp[2][2] = 1
dp[3][1+2] += dp[2][2] = 1
dp[3][2+2] += dp[2][2] = 1 - 选择dp[2]层中非0元素dp[2][1] = 1
- 缩进0到1个单位
- 更新答案
dp[3][0+2] += dp[2][1] = 1
dp[3][1+2] += dp[2][1] = 1 - 选择dp[2]层中非0元素dp[2][0] = 1
- 缩进0到0个单位
- 更新答案
dp[3][0+2] += dp[2][0] = 1
综上,我们发现了两条有用的信息
1. dp[3][0+2] += dp[2][3] + dp[2][2] + dp[2][1] + dp[2][0] ;
dp[3][1+2] += dp[2][3] + dp[2][2] + dp[2][1] ;
dp[3][2+2] += dp[2][3] + dp[2][2] ;
dp[3][3+2] += dp[2][3] ;
每个加的是个后缀和
2. +2偏移是接的长度,加新的、长度为l的fs结构时,会使结果整个向后推l位,前面填0
所以咱们操作的时候把它分成两步就很容易了
- 倒着加 dp[i] += dp[i+1]
- 后移l位,前面填0
显然也很容易直接把第一维压掉
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const ll DSY = 1000000000+7;
const int MAXN = 5000+5;
ll dp[MAXN];
int len;
bool iss[MAXN];
int main(void){
scanf("%d",&len);
for(int i=0;i<len;i++){
char str[2];
scanf("%s",str);
iss[i] = (str[0] == 's');
}
vector<int> blocks;
int fcnt = 0;
for(int i=0;i<len;i++){
if(iss[i]){
blocks.push_back(fcnt);
fcnt = 0;
}else{
fcnt++;
}
}
dp[0] = 1;
for(auto elm:blocks){
for(int i=5000;i>=0;i--){
dp[i] += dp[i+1];
dp[i] %= DSY;
}
for(int i=5000;i>=0;i--){
dp[i] = ( (i-elm >= 0 ) ? (dp[i-elm]) : (0) );
}
}
ll ans = 0;
for(int i=5000;i>=0;i--){
ans += dp[i];
}
ans %= DSY;
cout << ans;
return 0;
}
才疏学浅,如有错误,欢迎指正。