问题 E: OTTFFSSEN
时间限制: 1 Sec 内存限制: 512 MB提交: 91 解决: 34
[ 提交][ 状态][ 讨论版]
题目描述
他的老师为了调教他,就给他出了一道题:
求在区间[L,R]范围内的正整数中在十进制表示下不是0的数位的个数。
Aglove已经AFO太久太久了,况且他还要陪妹子数星星,他决定将这个光荣而伟大的任务委任给你,如果你能顺利完成,你将成为伟大的数数少年。
输入
输出
样例输入
23 233
样例输出
515
OTTFFSSEN
这道题不得不吐槽一下,我用数位DP活活杠了一个下午加上午的一个小时。终于打完了。
这道题貌似打法很多。可以打表,可以模拟,还可以数位DP。考试的时候就留了10分钟打这道题,于是乎打了最暴力的暴力,就水过了最基本的30%。剩下的30%是打表,20%是数位DP,20%貌似是,还有要加上高精度。
数位DP光看就看了好几个小时,终于弄懂了一点,基本就可以理解为从i-1位转移到i位,中间加上多加的那一位所改变的量即可。主要麻烦在怎么对非10^k次方数位进行处理上。这道题我设定的状态数组为f[i][j],共i位且第i为为j,允许前缀0。例如f[5][1]代表100000~199999,f[5][0]代表0~9999。那么转移也很清晰了:
f[i][0]=f[i-1][0~9],f[i][1]=f[i-1][0~9]+xp[i-1],2~9类推,xp[i]为10的i次方。
之后就是本题最大的难点了(在我看来),对零散数据即非10^k的处理,活活调了半个下午,第一次用的深搜,挂的没商量,然后听了王旭的,找了本题的原型:BZOJ1833。找到了一个模板,但该题与本题最大的不同即为该题所找的为0~9各个数的出现的次数,因此状态数组多一维,而且不允许前缀0。因此该题标程并不完全适合本题,因此在纠结了好几个小时后将之改为符合本题的处理方式。
首先先预处理出来该数的长度和各位的数字。然后ans=0,ans+=f[len][0~a[len]-1]。然后进行零散处理。
从len-1开始依次递减处理,假设该数为6524,则第一次处理为0~5999,第二次为6000~6499,第三次为6500~6519,第四次为6520~6524,处理的范围依次缩小,最终精确到个位(实际上是0位,因为根据本算法,若为一位数则无法向下精确)。求出本次精确位数内有几个零很简单,只要假设该数本来只有那几位就好了,然而这样下来会WA,原因很简单,没有“补位”。所谓“补位”,顾名思义,将之前求时少加的位加上,那么一次加多少位呢,len-i吗,不是。因为如120052156,就不用补那么多位,因此需要设定一个变量,记录到当前为为止有多少个非0位那么补多少个呢,补本次处理所覆盖的数字数,也就是(a[l-1]*xp[i-1])。做到这里就完成80%了,接下来是记录该数字最后一位非零数在哪,如200,用到目前为止的算法只能算到199,因此需要处理最后一位,只要将ans+=js即可,也正因如此,js++的条件为a[i+1]!=0且这个语句一定要放在la语句之前。核心代码就是这些,不到30行却坑了我一个下午,询问是只要求[0,r]-[0,l-1]的差即可。
虽然核心代码只有30几行,但坑了我一下午,在此鸣谢刘旭阳给我用来测试的程序和王旭给我的原型题!!!
#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<set>
#define V 10005
using
namespace
std;
int
n,k;
int
ans,a[V];
int
s[855][6555];
inline
void
update(
int
x,
int
y,
int
z)
{
for
(
int
i=x;i<=k;i+=i&(-i))
for
(
int
j=y;j<=5555;j+=j&(-j))
s[i][j]=max(s[i][j],z);
}
int
GG(
int
x,
int
y)
{
int
sk=0;
for
(
int
i=x;i>=2;i-=i&(-i))
for
(
int
j=y;j>=2;j-=j&(-j))
sk=max(sk,s[i][j]);
return
sk;
}
int
main()
{
//freopen("in.txt","r",stdin); freopen("out.txt","w",stdout);
//freopen("wbtree.in","r",stdin); freopen("wbtree.out","w",stdout);
cin>>n>>k;
for
(
int
i=1;i<=n;i++)
scanf
(
"%d"
,&a[i]);
//cin>>a[i];
k++;k++;
for
(
int
j=2;j<=k;j++)
update(j,a[1]+j,1);
int
t;
for
(
int
i=2;i<=n;i++)
{
for
(
int
j=k;j>=2;j--)
{
t=GG(j,a[i]+j)+1;
ans=max(ans,t);
update(j,a[i]+j,t);
}
}
cout<<ans<<endl;
//while(1);
return
0;
}