Codeforces 909C Python Indentation:树状数组优化dp
题目链接: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:
1 #include <iostream> 2 #include <stdio.h> 3 #include <string.h> 4 #define MAX_N 5005 5 #define MOD 1000000007 6 7 using namespace std; 8 9 int n; 10 int dp[MAX_N][MAX_N]; 11 char c[MAX_N]; 12 13 void update(int *dat,int k,int x) 14 { 15 while(k>0) 16 { 17 dat[k]=(dat[k]+x)%MOD; 18 k-=k&-k; 19 } 20 } 21 22 int query(int *dat,int k) 23 { 24 int sum=0; 25 while(k<=n) 26 { 27 sum=(sum+dat[k])%MOD; 28 k+=k&-k; 29 } 30 return (sum%MOD+MOD)%MOD; 31 } 32 33 void sec(int *dat,int l,int r,int x) 34 { 35 update(dat,r,x); 36 update(dat,l-1,-x); 37 } 38 39 int main() 40 { 41 cin>>n; 42 for(int i=1;i<=n;i++) cin>>c[i]; 43 memset(dp,0,sizeof(dp)); 44 sec(dp[1],1,1,1); 45 for(int i=1;i<=n;i++) 46 { 47 for(int j=1;j<=i;j++) 48 { 49 int now=query(dp[i],j); 50 if(now) 51 { 52 if(c[i]=='f') 53 { 54 sec(dp[i+1],j+1,j+1,now); 55 } 56 else 57 { 58 sec(dp[i+1],1,j,now); 59 } 60 } 61 } 62 } 63 int ans=0; 64 for(int i=1;i<=n;i++) 65 { 66 ans=(ans+query(dp[n],i))%MOD; 67 } 68 cout<<ans<<endl; 69 }