hihoCoder1033 交错和 数位DP

时间:2023-03-08 17:38:57

题目:交错和

链接:http://hihocoder.com/problemset/problem/1033#

题意:对于一个十进制整数x,令a0、a1、a2、...、an是x从高位到低位的数位,定义f(x)=a0-a1+a2-a3+...an,给出L、R、K,x在L到R之间,求所有满足:f(x)=k的x的和。(0 ≤ l ≤ r ≤ 10^18, |k| ≤ 100)

思路:

  L与R太大,连预处理的可能性都没有,很明显的数位DP。

  令dp[i][j]为精确的(有前导0)i 位,f(x)值为j 的x的和

  令dd[i][j]为精确的i 位,f(x)值为j 的x的个数。

  dp[1][i] = i (i从0-9)

  dd[1][i] = 1(i从0-9)

  当i为奇数时,最后一位前的符号位+,所以dp[i][j] = dp[i][j] + dp[i-1][j-u]*10 + u*dd[i-1][j-u] (u从0-9)

  dd[i][j]=dd[i][j] + dd[i-1][j-u] (u从0-9)

  当i为偶数时,将j-u改成j+u即可

  注意这里的dp求的和是有前导0的,比如3位数里098的f值为0-9+8也就是-1,所以dp[3][k]并不是0到1000里f值为k的数的和,因此我们要重新计算一个不含前导0的。

  令op[i][j]为i 位,f(x)值为j 的x的和

  令pp[i][j]为i 位,f(x)值为j 的x的个数。

  递推方法与dp一样,不同的是初始化,特别的令pp[1][0]=0,这样,等会递推出来的就是不包含前导0的,之后,为了方便,我们可以重新定义op[i][j]为前i 位,f(x)值为j 的x的和,pp也一样,进行下前缀和计算就可以了。

  解决完以上两步,这个问题便简单很多了,l到r,我们可以先算出0-r的,再减去0-l的,因此问题就转化成x属于0-l,f(x)值为k的x的和了,比如说3256,那么我们可以先算1000里有多少,1000-2000有多少,2000-3000有多少,再算3000-3100里有多少,3100-3200里有多少......1000里的直接就是op[3][k],1000到2000的,我们可以遍历u从0-9,现在就是1-u+?-?=k有多少了,那么?-?的值应该是k-1+u,也就是op[2][k-1+u],同时注意下细节就可以了。具体看代码。

  注:值得注意的是dd[0][0]=1,pp[0][0]=1

  

 #include<stdio.h>
#include<iostream>
using namespace std;
#define Mod 1000000007
typedef long long LL;
LL dp[][]={};
LL dd[][]={};
LL op[][]={};
LL pp[][]={};
int getPos(int val)
{
return val+;
}
void Init()
{
dd[][]=;
for(int i=;i<=;i++) dd[][getPos(i)]=,dp[][getPos(i)]=i;
for(int i=;i<=;i++)
{
if(i&)
{
for(int j=;j<;j++)
{
for(int k=j;k<=;k++)
{
dp[i][k]=(dp[i][k]+dp[i-][k-j]*%Mod+j*dd[i-][k-j]%Mod)%Mod;
dd[i][k]=(dd[i][k]+dd[i-][k-j])%Mod;
}
}
}
else
{
for(int j=;j<;j++)
{
for(int k=;k<=-j;k++)
{
dp[i][k]=(dp[i][k]+dp[i-][k+j]*%Mod+j*dd[i-][k+j]%Mod)%Mod;
dd[i][k]=(dd[i][k]+dd[i-][k+j])%Mod;
}
}
}
} for(int i=;i<=;i++) pp[][getPos(i)]=,op[][getPos(i)]=i;
pp[][getPos()]=;
for(int i=;i<=;i++)
{
if(i&)
{
for(int j=;j<;j++)
{
for(int k=j;k<=;k++)
{
op[i][k]=(op[i][k]+op[i-][k-j]*%Mod+j*pp[i-][k-j]%Mod)%Mod;
pp[i][k]=(pp[i][k]+pp[i-][k-j])%Mod;
}
}
}
else
{
for(int j=;j<;j++)
{
for(int k=;k<=-j;k++)
{
op[i][k]=(op[i][k]+op[i-][k+j]*%Mod+j*pp[i-][k+j]%Mod)%Mod;
pp[i][k]=(pp[i][k]+pp[i-][k+j])%Mod;
}
}
}
}
pp[][getPos()]=;
for(int i=;i<=;i++)
{
for(int j=;j<=;j++)
{
op[i][j]=(op[i][j]+op[i-][j])%Mod;
pp[i][j]=(pp[i][j]+pp[i-][j])%Mod;
}
}
} LL solve(LL x,LL p)
{
if(x<) return ;
LL tmp=x;
int bt[],bo=;
while(x)
{
bt[bo++]=x%;
x/=;
}
LL io=;
for(int i=;i<bo;i++)
io=io*;
LL sum=,k=,kk=;
for(int i=bo-;i>;i--)
{
if((bo-i)&)
{
for(int j=;j<bt[i];j++)
{
if(i==bo-&&j==)
{
sum=(sum+op[i][getPos(p)])%Mod;
}
else
{
for(int u=;u<=;u++)
{
sum=(sum+(kk*%Mod+j*+u)%Mod*io/%Mod*dd[i-][getPos(p+u-j-k)]%Mod+dp[i-][getPos(p+u-j-k)])%Mod;
}
}
}
k+=bt[i];
kk=(kk*%Mod+bt[i])%Mod;
}
else
{
for(int j=;j<bt[i];j++)
{
sum=(sum+(kk*%Mod+j)%Mod*io%Mod*dd[i][getPos(p+j-k)]%Mod+dp[i][getPos(p+j-k)])%Mod;
}
k-=bt[i];
kk=(kk*%Mod+bt[i])%Mod;
}
io/=;
}
if(bo&)
{
for(int i=;i<=bt[];i++)
if(p-k==i)
{
sum=(sum+kk*%Mod+i)%Mod;
}
}
else
{
for(int i=;i<=bt[];i++)
if(k-p==i) sum=(sum+kk*%Mod+i)%Mod;
}
return sum;
}
int main()
{
Init();
LL l,r,k;
cin>>l>>r>>k;
cout<<(solve(r,k)-solve(l-,k)+Mod)%Mod<<endl;
return ;
}

AC代码