[Contest1336]sequence

题面

Description

【题目描述】

    给定一个长度为n的由['0'..'9']组成的字符串s,v[i,j]表示由字符串s第i到第j位组成的十进制数字。

    将它的某一个上升序列定义为:将这个字符串切割成m段不含前导'0'的串,切点分别为k1,k2...km-1,使得v[1,k1]<v[k1+1,k2]<...<v[km-2,km-1]。

    请你求出该字符串s的上升序列个数,答案对 10^9+7 取模。

【输入数据】

    第一行一个整数n,表示字符串长度;

    第二行n个['0'..'9']内的字符,表示给出的字符串s。

【输出数据】

    仅一行表示给出字符串s的上升序列个数对10^9+7取模的值。

【样例输入1】

6

123434

【样例输出1】

8

【样例输入2】

8

20152016

【样例输出2】

4

【数据范围】

    对于30%的数据满足:n<=10;

    对于100%的数据满足:n<=5000。

题意

一个有n个数的序列,将它分成若干段,要求每段不含有前导零,且分段后形成的m个数单调递增。

题解

对于30%的数据,看到这么小的n,当然是大暴力啦hhhhhh

#include<iostream>
using namespace std;
int n,ans;
char ch[30];
unsigned long long toi(int l,int r){//计算分出来的数
    unsigned long long res=0;
    for(register int i=l;i<=r;i++)res=res*10ll+(unsigned long long)ch[i]-'0';
    return res;
}
void dfs(int u,int lp,unsigned long long li){
    if(u==n+1){
        ans++;
        return;
    }
    unsigned long long num=toi(lp+1,u);
    if(num>li&&ch[u+1]!='0')dfs(u+1,u,num);//注意前导零
    if(u!=n)dfs(u+1,lp,li);
}
int main(){
//  freopen("1.txt","r",stdin);
    scanf("%d%s",&n,ch+1);
    dfs(1,0,0);
    printf("%d",ans);
}
View Code

100%的数据,考虑dp。

看到序列,自然想到关于序列的东西,一番摸索后发现可以用lcp做。

设$lcp[i][j]$为$i$下标开始的后缀和$j$下标开始的后缀的lcp。

然后枚举断点和区间长度,有请dp

设$f[i][j]$为将$[i,i+j)$作为一段可行的方案数,将$[i,i+j)$分为一段后,下一段的起点为$i+j$,长度>=$j$,得到递推式:

如果当前段比下一段小,$f[i][j]=f[i+j][j]+f[i+j][j+1]+......+f[i+j][n-i]$

否则,$f[i][j]=f[i+j][j+1]+f[i+j][j+2]+......+f[i+j][n-i]$

比较大小可以通过lcp快速得出,而递推可以通过维护后缀和达到$O(n^{2})$

#include<iostream>
using namespace std;
int n,f[5005][5005],lcp[5005][5005],mod=1e9+7;
char ch[5005];
int main(){
    scanf("%d%s",&n,ch+1);
    for(int i=n;i>=1;i--){//lcp预处理
        for(int j=i+1;j<=n;j++){
            if(ch[i]==ch[j])lcp[i][j]=lcp[i+1][j+1]+1;
        }
    }
    for(int i=n;i>=1;i--){
        if(ch[i]=='0')continue;//前导零
        f[i][n-i+1]=1;
        for(int j=1;j<=n-i;j++){
            int t=min(lcp[i][i+j],j-1);
            if(ch[i+t]<ch[i+j+t])f[i][j]=f[i+j][j];//比较
            else f[i][j]=f[i+j][j+1];
        }
        for(int j=n-i;j>=1;j--)f[i][j]=(f[i][j]+f[i][j+1])%mod;//后缀和
    }
    printf("%d",f[1][1]);
}
View Code

猜你喜欢

转载自www.cnblogs.com/Evan704/p/11396595.html