codeforces GoodBye2015 D.New Year and Ancient Prophecy (dp+lcp+树状数组)

来源:互联网 发布:淘宝运营怎么做 编辑:程序博客网 时间:2024/06/07 14:57

题目:http://codeforces.com/contest/611/problem/D

题意:


D. New Year and Ancient Prophecy
time limit per test
2.5 seconds
memory limit per test
512 megabytes
input
standard input
output
standard output

Limak is a little polar bear. In the snow he found a scroll with the ancient prophecy. Limak doesn't know any ancient languages and thus is unable to understand the prophecy. But he knows digits!

One fragment of the prophecy is a sequence of n digits. The first digit isn't zero. Limak thinks that it's a list of some special years. It's hard to see any commas or spaces, so maybe ancient people didn't use them. Now Limak wonders what years are listed there.

Limak assumes three things:

  • Years are listed in the strictly increasing order;
  • Every year is a positive integer number;
  • There are no leading zeros.

Limak is going to consider all possible ways to split a sequence into numbers (years), satisfying the conditions above. He will do it without any help. However, he asked you to tell him the number of ways to do so. Since this number may be very large, you are only asked to calculate it modulo 109 + 7.

Input

The first line of the input contains a single integer n (1 ≤ n ≤ 5000) — the number of digits.

The second line contains a string of digits and has length equal to n. It's guaranteed that the first digit is not '0'.

Output

Print the number of ways to correctly split the given sequence modulo 109 + 7.

Examples
input
6123434
output
8
input
820152016
output
4
Note

In the first sample there are 8 ways to split the sequence:

  • "123434" = "123434" (maybe the given sequence is just one big number)
  • "123434" = "1" + "23434"
  • "123434" = "12" + "3434"
  • "123434" = "123" + "434"
  • "123434" = "1" + "23" + "434"
  • "123434" = "1" + "2" + "3434"
  • "123434" = "1" + "2" + "3" + "434"
  • "123434" = "1" + "2" + "3" + "4" + "34"

Note that we don't count a split "123434" = "12" + "34" + "34" because numbers have to be strictly increasing.

In the second sample there are 4 ways:

  • "20152016" = "20152016"
  • "20152016" = "20" + "152016"
  • "20152016" = "201" + "52016"
  • "20152016" = "2015" + "2016"

给你一个只包含'0'~'9'的长度为n(n<=5000)字符串,你可以将这个串随意划分,但是必须满足前一个数严格比后一个数小,而且划分后的每一个数不能有前导0。求划分的方案数。


分析:

定义:dp[i][j]

dp[x][y]表示以字符串str[x..y]结尾的方案数。

那么sigma(dp[i][n]) (1<=i<=n)即为答案。


状态转移:

由dp[x][y]推向别的状态。

①如果后一个串第一个字符为零,那么以后一个串结尾的方案数为0


②如果后一个串的长度小于str[x..y]的长度,那么以后一个串结尾的方案数为0


③如果后一个串的长度和str[x..y]的长度相等,再要比较后一个串的和str[x..y]的大小。

直接比较的话,要O(n)的时间复杂度,我们可以先预处理出所有后缀的最长公共前缀lcp。

推的话还是比较简单,如果str[i]==str[j],那么lcp[i][j]=lcp[i+1][j+1]+1(lcp[i][j]表示分别以i和j开头的两个后缀的最长公共前缀),再比较串的大小就只需O(1)的时间复杂度了。


④如果后一个串的长度比str[x..y]的长度长,那么后一个串的方案数加上这个串的方案数。

注意这里转移就需要一个循环了,因为后面比str[x..y]长的串有很多,str[y+1...k](k<=n && k-y>y-x+1)。

如果加一层循环,那么复杂度到了O(n^3)。

注意到后面的串可以表示层一个连续的区间,所以可以用线段树后者树状数组记录dp。(线段树爆内存...)

tree[n][n],表示n个树状数组,每一个维护一个后缀,比如tree[3],维护以3开头的后缀。


总的时间复杂度O(N*N*logN)。

代码:

#include <bits/stdc++.h>using namespace std;typedef long long LL;typedef unsigned long long ULL;const LL INF = 1e9+7;const LL MINT = ~0u>>1;#define lson l,m,rt<<1#define rson m+1,r,rt<<1|1 const int maxn = 5005;const int mod = 1e9+7;char str[maxn];short lcp[maxn][maxn],n;int tree[maxn][maxn];void add(int &a,int b){a+=b;while(a<0)a+=mod;while(a>=mod)a-=mod; }void update(int *s,int L,int R,int c){for(int i=L;i<=n;i+=(i&-i))add(s[i],c);for(int i=R+1;i<=n;i+=(i&-i))add(s[i],-c);}int query(int *s,int pos){int ret(0);for(int i=pos;i>0;i-=(i&-i))add(ret,s[i]);return ret;}void makeLcp(){for(int i=n;i>=1;i--)for(int j=n;j>=1;j--) if(str[i]==str[j])lcp[i][j]=lcp[i+1][j+1]+1;}bool cmp(int x1,int y1,int x2,int y2){int len=y1-x1+1,LCP=lcp[x1][x2];if(LCP<len && str[x1+LCP]<str[x2+LCP])return 1;return 0;}void solve(){update(tree[1],1,n,1);for(int i=1;i<=n;i++){for(int j=i;j<=n;j++) if('0'!=str[j+1]){int len = j-i+1;int dp = query(tree[i],j); if(dp==0)continue ; if(j+len<=n && cmp(i,j,j+1,j+len)){update(tree[j+1],j+len,j+len,dp);//printf("[%d %d] update %d ... [%d %d]  dp:%d\n ",i,j,j+1,j+len,j+len,dp);}if(j+len+1<=n){update(tree[j+1],j+len+1,n,dp);//printf("[%d %d] update %d ... [%d %d]   dp:%d\n",i,j,j+1,j+len+1,n,dp);}}}int ans=0;for(int i=1;i<=n;i++)add(ans,query(tree[i],n));cout<<ans;}int main(){scanf("%d%s",&n,str+1);makeLcp();solve();return 0;}



0 0
原创粉丝点击