zoukankan      html  css  js  c++  java
  • Codeforces 611D New Year and Ancient Prophecy DP

    题意:

    把一个字符串分割成多个小串,小串组成严格递增序列,n<=5000

    这是一个DP。

    s代表原串

    dp[i][j]代表当前到i位置最后一个串是以j为开头的方案数。答案就是dp[n][1]+...+dp[n][n]

    很容易得到dp[i][j]=dp[j-1][k]+dp[j-1][k+1]……dp[j-1][j-1]    (i-j=j-k) 如果s[k-1....j-1] >=s[j....i], dp[i][j]+=dp[j-1][k-1]

    这样dp的递推式就有了,很容易发现dp[i][j]的递推式是一个前缀和,假设我比较s[k-1....j-1]和s[j....i]是O(1) 那么就可以在n^2的复杂度里得到答案

    所以问题转化为如何预处理以x为开头的串和以y为开头的串的大小(x<y)

    令r[x][y]为以x开头的串和以y开头的串经过多少长度分出大小,is[x][y]为true代表x小,为false代表y小

    if(r[x-1][y-1]>0) r[x][y]=r[x-1][y-1]-1,is[x][y]=is[x-1][y-1];

    else 暴力跑出r[x][y]    

    所以对于每一个r[1][y]我们得出r[2][y+1]...r[n-y+1][n]的复杂度是O(n)

    所以求出r[1][1]....r[1][n]

    然后有了所有的is(x,y) 就可以O(1)比较以x和y开头的子串大小

    具体代码

    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<map>
    #include<cmath>
    #include<algorithm>
    using namespace std;
    const int N=5005;
    const long long MOD=1000000007;
    long long dp[N][N];
    int r[N][N];
    bool is[N][N];
    char s[N];
    char a[N],b[N];
    int l1,l2,n;
    int main()
    {
        int i,j,k;
        while(scanf("%d",&n)!=EOF)
        {
            scanf("%s",s+1);
            for(i=1;i<=n;i++) dp[i][1]=1;
            for(j=2;j<=n;j++)
            {
                is[1][j]=false;
                for(k=0;k+j<=n;k++)
                {
                    if(s[1+k]>s[j+k]) {is[1][j]=false;r[1][j]=k;break;}
                    if(s[1+k]<s[j+k]) {is[1][j]=true;r[1][j]=k;break;}
                }
                if(k+j>n) r[1][j]=n+1;
            }
            for(k=1;k<n;k++)
            {
                for(i=2;i+k<=n;i++)
                {
                    if(r[i-1][i+k-1]) {is[i][i+k]=is[i-1][i+k-1];r[i][i+k]=r[i-1][i+k-1]-1;}
                    else
                    {
                        is[i][i+k]=false;
                        for(j=0;i+j<=n&&i+k+j<=n;j++)
                        {
                            if(s[i+j]>s[i+k+j]) {is[i][i+k]=false;r[i][i+k]=j;break;}
                            if(s[i+j]<s[i+k+j]) {is[i][i+k]=true;r[i][i+k]=j;break;}
                        }
                        if(i+k+j>n) r[i][i+k]=n+1;
                    }
                }
            }
            for(i=2;i<=n;i++)
            {
                k=i;
                if(s[i]=='0') continue;
                long long sum=0;
                for(j=i;j<=n;j++)
                {
                   if(k>0) sum+=dp[i-1][k];
                   if(sum>=MOD) sum%=MOD;
                   k--;
                   dp[j][i]+=sum;
                   if(k>0&&s[k]=='0') continue;
                   int flag=0;
                   if(k>0)
                   {
                     if(is[k][i]==true&&r[k][i]+k<i) flag=1;
                   }
                   if(flag) dp[j][i]+=dp[i-1][k];
                   if(dp[j][i]>=MOD) dp[j][i]%=MOD;
                }
            }
            long long ans=0;
            for(i=1;i<=n;i++)
            {
                ans+=dp[n][i];
                if(ans>=MOD) ans%=MOD;
            }
            cout<<ans<<endl;
        }
        return 0;
    }
  • 相关阅读:
    JS的(new Date()).toLocaleString,系统设置为24小时,仍然显示为12小时。
    js中获取时间new date()的用法
    Warning: require(): open_basedir restriction in effect. 宝塔错误
    未定义数组下标: 0
    layui 弹层组件文档
    TP5模版中的运算符
    tp5倒计时还有几天几时几分
    【原创】tp5软件过期提醒
    a标签的title属性 换行
    电子签章盖章之jQuery插件jquery.zsign
  • 原文地址:https://www.cnblogs.com/Woo95/p/5094637.html
Copyright © 2011-2022 走看看