codeforces 611D New Year and Ancient Prophecy

f[i = 以i结尾][j = 长度为j] = 方案数。

f[i][j] = sum{ f[i-j][k] , k < j || (k == j && s(i-j+1,j) > s(i-2*j+1,j) ) }

转移为O(N^3)需要优化,

对于k < j,递推g[i][j] = sum(f[i][k], k <= j)。

对于k == j,有O(N^2)个后缀,可以用二维数组lcp[i][j]递推i和j开头的最长公共前缀。

(后缀数组倍增大概也可以做的,用memcpy都可以过的,常数还比较小,极限数据1481ms。

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;

const int N = 5e3+5, mod = 1e9+7;
int f[N][N], g[N][N];
char s[N];

int lcp[N][N];

bool bigger(int i, int j, int len)
{
    if(lcp[i][j] >= len) return false;
    else {
        int c = lcp[i][j];
        return s[i+c] > s[j+c];
    }
}

//#define LOCAL
int main()
{
#ifdef LOCAL
    freopen("in.txt","r",stdin);
#endif
    int n, i, j;
    scanf("%d%s", &n, s+1);
    if(s[1] == '0'){ puts("0"); return 0; }
    for(i = n; i > 0; i--){
        for(j = n; j > 0; j--){
            if(s[i] == s[j]) lcp[i][j] = lcp[i+1][j+1]+1;
        }
    }

    for(i = 1; i <= n; i++){
        for(j = 1; j < i; j++) {
            g[i][j] = g[i][j-1];
            if(s[i-j+1] != '0'){
                f[i][j] = g[i-j][min(j-1,i-j)];
                if(f[i-j][j] && bigger(i-j+1,i-j-j+1,j)){
                    f[i][j] += f[i-j][j];
                    if(f[i][j] >= mod) f[i][j] -= mod;
                }
                g[i][j] += f[i][j];
                if(g[i][j] >= mod) g[i][j] -= mod;
            }
        }
        f[i][i] = 1;
        g[i][i] = g[i][i-1] + 1;
        if(g[i][i] == mod) g[i][i] = 0;
    }
    printf("%d\n",g[n][n]);
    return 0;
}

 

posted @ 2015-12-31 14:43  陈瑞宇  阅读(291)  评论(0编辑  收藏  举报