您的位置:首页 > 运维架构

Codeforces_611D:New_Year_and_Ancient_Prophecy(DP+树状数组)

2017-01-14 23:25 447 查看
题意是给定一个n位数字,你现在可以将它分隔成若干个长度更短的数字,例如1234567可以拆分成1234和567两个数字,现在规定一个拆分方案合法当且仅当①拆分出的数字按顺序严格递增②拆除的每个数字是正整数③拆分出的每个数字没有前导0.现在给定n和原来的n位数字,问有多少种合法的拆分方案,答案mod 1e9+7.

设原来的n位数字分别存储在a[1..n]中,设c[i][j]表示的是当前分隔到第j位且最后一个分隔出的数字是a[i...j]的方案数

那么可以得到一种递推关系c[i][j]=sum(c[k][i-1])当a[k...i-1]<a[i...j]时,但很明显直接这样暴力求解是O(n^4),因为递推是O(n^3),而每次判断数字大小关系又得乘上O(n),合计为O(n^4),明显TLE.

那么代入下面的这个优化,考虑到这么一个很显然的论断,当两个数字没有前导0且数位长度不等时,则长度更短的那个数字一定更小,所以到这儿可以将以某个位置结束且长度为某个值的方案数提前算好,这里用树状数组d[i][j]来实现,sum(i,j)表示最后当前最后一位分隔到位置i,且最后一个分隔出的数字长度<=j的方案数.

但是到这里还是不够,因为除了上面一段所说的数位不等的情况优化到了很低的时间复杂度之外,想必还有数位相等的情况,但是如果暴力判断的话,综合复杂度还是会达到O(n^3),这时便需要用到另外的一个预处理,设b[i][j]表示a[i...]和a[j...]在连续的b[i][j]位里完全相同,这样在b[i][j]+1位两者不等或者超出了边界,这样当需要判断a[k..i-1]与a[i...j]的大小关系时,只需要判断b[k][i]与j-i+1的大小关系,以及a[k+b[k][i]]和a[i+b[k][i]]的大小关系,这样在o(n^2)的预处理后便能以O(1)的复杂度完成数值比较.

下面附上程序:

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
using namespace std;
#define ll long long
const int N=5002;
const int M=1000000007;
int n,a
,b

,c

,d

;
int add(int x,int y,int k){while(y<=n){d[x][y]=((d[x][y]+k)%M+M)%M;y+=y&(-y);}return 0;}
int sum(int x,int y){int k=0;while(y>0){k=(k+d[x][y])%M;y-=y&(-y);}return k;}
int main()
{
int i,j,k;
scanf("%d",&n);
for(i=1;i<=n;i++)scanf("%1d",a+i);
for(i=n;i>=1;i--)
for(j=n;j>=i;j--)b[i][j]=(a[i]==a[j])*(b[i+1][j+1]+1);
for(i=1;i<=n;i++)
for(j=i;j<=n;j++)
{
if(a[i]==0){c[i][j]=0;continue;}
if(i==1)c[i][j]=1;else c[i][j]=0;
c[i][j]=(c[i][j]+sum(i-1,j-i))%M;
if((k=i-(j-i+1))>=1&&b[k][i]<j-i+1&&a[k+b[k][i]]<a[i+b[k][i]])c[i][j]=(c[i][j]+c[k][i-1])%M;
add(j,j-i+1,c[i][j]);
}
k=0;for(i=1;i<=n;i++)k=(k+c[i]
)%M;
cout<<k;
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息