Codeforces 909C Python Indentation:树状数组优化dp
C. Python Indentation
time limit per test2 seconds
memory limit per test256 megabytes
inputstandard input
outputstandard output
In Python, code blocks don't have explicit begin/end or curly braces to mark beginning and end of the block. Instead, code blocks are defined by indentation.
We will consider an extremely simplified subset of Python with only two types of statements.
Simple statements are written in a single line, one per line. An example of a simple statement is assignment.
For statements are compound statements: they contain one or several other statements. For statement consists of a header written in a separate line which starts with "for" prefix, and loop body. Loop body is a block of statements indented one level further than the header of the loop. Loop body can contain both types of statements. Loop body can't be empty.
You are given a sequence of statements without indentation. Find the number of ways in which the statements can be indented to form a valid Python program.
Input
The first line contains a single integer N (1 ≤ N ≤ 5000) — the number of commands in the program. N lines of the program follow, each line describing a single command. Each command is either "f" (denoting "for statement") or "s" ("simple statement"). It is guaranteed that the last line is a simple statement.
Output
Output one line containing an integer - the number of ways the given sequence of statements can be indented modulo 109 + 7.
Examples
inputCopy
4
s
f
f
s
outputCopy
1
inputCopy
4
f
s
f
s
outputCopy
2
Note
In the first test case, there is only one way to indent the program: the second for statement must be part of the body of the first one.
simple statement
for statement
for statement
simple statement
In the second test case, there are two ways to indent the program: the second for statement can either be part of the first one's body or a separate statement following the first one.
for statement
simple statement
for statement
simple statement
or
for statement
simple statement
for statement
simple statement
题意:
Python是没有大括号来表明语句块的,而是用严格的缩进来体现
现在有简化版的Python,只有两种语句,
(1)'s'语句:Simple statements. 相当于一般语句。
(2)'f'语句:For statements. 相当于for循环,并且规定它的循环体不能为空。
现在给你一个没有缩进的Python程序,共n行,问你添加缩进后,有多少种合法且不同的Python程序。
思路: dp,定义状态:dp[i][j] 考虑到第i行,并且第i行的缩进有j个tab时的合法方案数。答案就是 sum { dp[n-1][0 ~ n-1] }
行号从0开始,并且对于第i行来说,它的缩进最多有i个tab。
状态转移:
当前第i行为‘f' ,则第i+1行的缩进只能为j+1
dp[i+1][j+1]+=dp[i][j];
或者第i行为's' ,则第i+1行的缩进可以为[0,j] 中任意一种、
dp[i+1][0 to j] +=dp[i][j];
初始条件:
dp[0][0]=1
树状数组优化:
按照上面的转移方程去写,枚举状态是nn,第二种转移的时间复杂度是n,所以时间复杂度是nn*n,肯定会tle的,
所以我们考虑用树状数组来实现转移第2种情况,利用差分数组,进行区间修改,区间/单点查询。
时间复杂度就是 nnlogn
另外bit从下标1开始,所以之前所有下标都要加1
Ps:还有一个值得注意的是,区间修改时,add(data,r+1,-x); ,给加了负值,会出现负数,所以取模的时候一定要是( dp[id] +mod )%mod ,不注意很容易wa
细节见代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <vector>
#include <iomanip>
#define ALL(x) (x).begin(), (x).end()
#define rt return
#define dll(x) scanf("%I64d",&x)
#define xll(x) printf("%I64d\n",x)
#define sz(a) int(a.size())
#define all(a) a.begin(), a.end()
#define rep(i,x,n) for(int i=x;i<n;i++)
#define repd(i,x,n) for(int i=x;i<=n;i++)
#define pii pair<int,int>
#define pll pair<long long ,long long>
#define gbtb ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define MS0(X) memset((X), 0, sizeof((X)))
#define MSC0(X) memset((X), '\0', sizeof((X)))
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define eps 1e-6
#define gg(x) getInt(&x)
#define chu(x) cout<<"["<<#x<<" "<<(x)<<"]"<<endl
using namespace std;
typedef long long ll;
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
ll lcm(ll a,ll b){return a/gcd(a,b)*b;}
ll powmod(ll a,ll b,ll MOD){ll ans=1;while(b){if(b%2)ans=ans*a%MOD;a=a*a%MOD;b/=2;}return ans;}
inline void getInt(int* p);
const int maxn=1000010;
const int inf=0x3f3f3f3f;
/*** TEMPLATE CODE * * STARTS HERE ***/
int n;
const ll mod=1e9+7ll;
ll dp[5005][5005];
char s[maxn];
int lowbit(int x)
{
return x&(-x);
}
void add(ll *data,int id,ll x)
{
while(id<=n)
{
data[id]+=x;
data[id]=(data[id]+mod)%mod;
id+=lowbit(id);
}
}
ll query(ll *data,int x)
{
ll res=0ll;
while(x)
{
res=(res+data[x])%mod;
x-=lowbit(x);
}
return res;
}
void update(ll *data,int l,int r,ll x)
{
add(data,l,x);
add(data,r+1,-x);
}
int main()
{
//freopen("D:\\common_text\\code_stream\\in.txt","r",stdin);
//freopen("D:\\common_text\code_stream\\out.txt","w",stdout);
gbtb;
cin>>n;
repd(i,1,n)
{
cin>>s[i];
}
MS0(dp);
update(dp[1],1,1,1);
repd(i,1,n-1)
{
repd(j,1,i)
{
ll now=query(dp[i],j);
if(now)
{
if(s[i]=='f')
{
update(dp[i+1],j+1,j+1,now);
}else
{
update(dp[i+1],1,j,now);
}
}
}
}
ll ans=0ll;
repd(i,1,n)
{
// chu(query(dp[n],i));
ans=(ans+query(dp[n],i))%mod;
}
cout<<ans<<endl;
return 0;
}
inline void getInt(int* p) {
char ch;
do {
ch = getchar();
} while (ch == ' ' || ch == '\n');
if (ch == '-') {
*p = -(getchar() - '0');
while ((ch = getchar()) >= '0' && ch <= '9') {
*p = *p * 10 - ch + '0';
}
}
else {
*p = ch - '0';
while ((ch = getchar()) >= '0' && ch <= '9') {
*p = *p * 10 + ch - '0';
}
}
}