題目連結: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 }