[Contest1336]sequence
Posted evan704
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[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);
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]);
以上是关于[Contest1336]sequence的主要内容,如果未能解决你的问题,请参考以下文章
LightOJ - 1336 - Sigma Function(质数分解)