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;
}
时间: 2024-12-10 21:52:56

Codeforces 611D New Year and Ancient Prophecy DP的相关文章

Codeforces 611D.New Year and Ancient Prophecy (dp + lcp)

题目链接: http://codeforces.com/problemset/problem/611/D 题意: 长为n的只有数字组成的字符串(n<=5000),问能分割成多少组数字,这些数字里不含前导0,且数字的大小满足严格单调递增 思路: from: http://blog.csdn.net/qwb492859377/article/details/50445450  qwb orz 最难的地方,就是如何去快速判断两个数字的大小谁大谁小呢? 我们先来讲下最长公共前缀lcp的定义.如果有串A和

codeforces 161D - Distance in Tree(树形dp)

题目大意: 求出树上距离为k的点对有多少个. 思路分析: dp[i][j] 表示 i 的子树中和 i 的距离为 j 的点数有多少个.注意dp[i] [0] 永远是1的. 然后在处理完一颗子树后,就把自身的dp 更新. 更新之前更新答案. 如果这颗子树到 i 有 x 个距离为j的.那么答案就要加上 dp[i] [ k-j-1] * x; #include <iostream> #include <cstdio> #include <cstring> #include &l

CodeForces 258B Little Elephant and Elections 数位DP

前面先用数位DP预处理,然后暴力计算组合方式即可. #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <climits> #include <string> #include <iostream> #include <map> #include <cstdlib> #include

Codeforces 437E The Child and Polygon(区间DP)

题目链接:Codeforces 437E The Child and Polygon 题目大意:给出一个多边形,问说有多少种分割方法,将多边形分割为多个三角形. 解题思路:首先要理解向量叉积的性质,一开始将给出的点转换成顺时针,然后用区间dp计算.dp[i][j]表示从点i到点j可以有dp[i][j]种切割方法.然后点i和点j是否可以做为切割线,要经过判断,即在i和j中选择的话点k的话,点k要在i,j的逆时针方. #include <cstdio> #include <cstring&g

Codeforces 219D. Choosing Capital for Treeland (树dp)

题目链接:http://codeforces.com/contest/219/problem/D 树dp 1 //#pragma comment(linker, "/STACK:102400000, 102400000") 2 #include <algorithm> 3 #include <iostream> 4 #include <cstdlib> 5 #include <cstring> 6 #include <cstdio&

CodeForces Round#229 DIV2 C 递推DP

对这道题目也只好说呵呵了,没注意k的范围最大才10,所以昨晚纠结了很久,没什么好的方法来处理,后来无奈想去翻翻题解,发现人家开头就来了句,因为k的范围比较小 所以.........我只好暂停马上回头看看题目,是的,k比较小所以完全可以先在询问前预处理DP一遍, DP就比较清晰了,dp[i][j]  (i>=0 && i<k,,,,j>=i && j <=n)代表意义呢 以i为开头的  区间[1,j]注意 是 1~j的 所需要的操作数,题目问的是最小操

Codeforces Beta Round #1 C. Ancient Berland Circus

果然Java还是不靠谱啊,一个NaN把我整了半天~~ 题目大意: 有一个正多边形,给出任意三个顶点的坐标,求这个正多边形的最小面积. 解题思路: 首先要知道这三个顶点组成的三角形的外接圆一定是这个正多边形的外接圆. 用过计算出三角形的三边长,可以计算出三角型面积,进而推出外接圆半径. 可以得到三个圆心角,找出最大公约数,那就是最大角度. 就可以计算出多边形面积了~~ 下面是代码: import java.text.DecimalFormat; import java.util.Scanner;

codeforces 235B Let&#39;s Play Osu! 概率dp

题意:给定n表示有n个格子,下面每个格子为O的概率是多少.对于一段连续 x 个O的价值就是 x^2 ;求获得的价值的期望是多少. 思路:n^2=n×(n-1)+n,设ai为第i段连续O的长度,∑ai^2 = ∑[ ai+ ai*(ai-1) ] = ∑ ai*(ai-1) + ∑ai = ∑ C(ai, 2)*2 + ∑ai,那么问题可以转 化为求长度大于1的连续段数*2+O的个数的总期望. ∑ai我们可以理解为O的总个数,所以它的期望为∑pi: C(ai, 2)*2我们可以认 为是连续ai个O

Codeforces 235B Let&#39;s Play Osu! (概率dp求期望+公式变形)

B. Let's Play Osu! time limit per test:2 seconds memory limit per test:256 megabytes You're playing a game called Osu! Here's a simplified version of it. There are n clicks in a game. For each click there are two outcomes: correct or bad. Let us deno