Codeforces 909C Python Indentation:树状数组优化dp

时间:2021-12-14 07:32:03

题目链接:http://codeforces.com/contest/909/problem/C

题意:

  Python是没有大括号来标明语句块的,而是用严格的缩进来体现。

  现在有一种简化版的Python,只有两种语句:

    (1)'s'语句:Simple statements. 相当于一般语句。

    (2)'f'语句:For statements. 相当于for循环,并且规定它的循环体不能为空。

  然后给你一段没有缩进的Python程序,共n行(n <= 5000)。

  问你添加缩进后,有多少种合法且不同的Python程序。

题解:

  表示状态:

    dp[i][j] = numbers

    考虑到第i行,并且第i行的缩进有j个Tab时的合法方案数。

  找出答案:

    ans = ∑ dp[n-1][0 to n-1]

    行号从0开始标。并且对于第i行来说,它的缩进最多有i个Tab。

  如何转移:

    两种情况。

    当前为dp[i][j](用顺推)。

    (1)第i行为'f',则第i+1行的缩进只能为j+1。

      dp[i+1][j+1] += dp[i][j]

    (2)第i行为's',则第i+1行的缩进可以为[0,j]中的任意一种。

      dp[i+1][0 to j] += dp[i][j]

  边界条件:

    dp[0][0] = 1

    第0行的缩进只能为0。

  树状数组优化:

    如果按照上面的方程直接去写的话,枚举状态为O(N^2),转移的第二种情况复杂度为O(N)。

    所以最坏情况下为O(N^3),对于N = 5000肯定炸了……

    所以考虑用树状数组来实现转移的第二种情况,也就是区间加法和单点查询。

    于是总复杂度变为O(N^2*logN)。

    另外,树状数组下标从1开始,所以之前所有的下标都要+1。

  update:

    其实顺推也可以用差分优化掉一个n的啊……

    (打比赛的时候人是瓷的……)

AC Code:

 #include <iostream>
#include <stdio.h>
#include <string.h>
#define MAX_N 5005
#define MOD 1000000007 using namespace std; int n;
int dp[MAX_N][MAX_N];
char c[MAX_N]; void update(int *dat,int k,int x)
{
while(k>)
{
dat[k]=(dat[k]+x)%MOD;
k-=k&-k;
}
} int query(int *dat,int k)
{
int sum=;
while(k<=n)
{
sum=(sum+dat[k])%MOD;
k+=k&-k;
}
return (sum%MOD+MOD)%MOD;
} void sec(int *dat,int l,int r,int x)
{
update(dat,r,x);
update(dat,l-,-x);
} int main()
{
cin>>n;
for(int i=;i<=n;i++) cin>>c[i];
memset(dp,,sizeof(dp));
sec(dp[],,,);
for(int i=;i<=n;i++)
{
for(int j=;j<=i;j++)
{
int now=query(dp[i],j);
if(now)
{
if(c[i]=='f')
{
sec(dp[i+],j+,j+,now);
}
else
{
sec(dp[i+],,j,now);
}
}
}
}
int ans=;
for(int i=;i<=n;i++)
{
ans=(ans+query(dp[n],i))%MOD;
}
cout<<ans<<endl;
}