题目:HDU 4507
一定要耐心看下去,你可以学会,博主在做这道题时,原以为不可能做的出来,但其实就是一个耐心的问题,相信自己,一定可以!
涉及知识:数位dp,模运算
思想:计数数位dp转为求和
难度:数位dp进阶
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=1e9+7;//模数
const int LEN=21;//长度
int dig[LEN];//每一位的数字
ll p[LEN];//每一位数字加权的值(这里是*10相当于把每位数转换为十进制)
struct node
{
ll cnt,sum,sqr;//分别代表计数,数字的和,数字的平方和(因为后面发现求平方和会用到数字的和)
node(ll cnt,ll sum,ll sqr):cnt(cnt),sum(sum),sqr(sqr){}//用node构造函数(有参数),把cnt位置的实参传给结构体中的cnt位置
node(){cnt=-1;sum=0;sqr=0;}//利用node构造函数(无参数),执行大括号内的语句,相当于给dp赋值;
}dp[LEN][10][10];//位数上限为LEN,后面为状态
void init()//初始化 给权值赋值
{
p[0]=1;
for(int i=1;i<LEN;i++)
{
p[i]=(p[i-1]*10)%mod;
}
}
node dfs(int pos,int dval,int val,bool limit)//记忆化搜索
{
if(pos==-1) //最后一位
{
return dval!=0&&val!=0?node(1,0,0):node(0,0,0);//满足条件就计数1,因为没有位了,所以sum和sqr自然是0;
}
if(!limit&&dp[pos][dval][val].cnt!=-1) return dp[pos][dval][val];//不是逼上限这种特殊情况且有记忆
int up=limit?dig[pos]:9;//根据是否逼上限判断是不是全范围遍历
node ans;
ans.cnt=ans.sum=ans.sqr=0; //结构体版本ans( ̄▽ ̄)~*
for(int i=0;i<=up;i++)
{
if(i==7) continue;//含7就直接跳过了~
node next=dfs(pos-1,(dval+i)%7,(val*10+i)%7,limit&&i==up);//循环寻找后面位的信息(每次循环都不同哦)
ans.cnt=(ans.cnt+next.cnt)%mod;//计数的原理就是基本的数位dp原理
//以下要重建数字 拆分成(i*权值+后面的部分 )
ans.sum=(ans.sum+next.sum+(p[pos]*i)%mod*next.cnt)%mod; //后面位+当前位所占的权值 (p[pos]*i)乘上当前位出现的次数(即贡献数)
ans.sqr=(ans.sqr+next.sqr)%mod; //后面部分的平方
ans.sqr=(ans.sqr+(2*p[pos]*i)%mod*next.sum)%mod;//这里涉及到一个分配率,把 (2*p[pos]*i)提出来,发现后面就是后面部分的sum
ans.sqr=(ans.sqr+(i*i*p[pos])%mod*p[pos]%mod*next.cnt)%mod;//(i*权值)的平方
}
if(!limit) dp[pos][dval][val]=ans;//没有逼到上限就有意义
return ans;
}
//取位并计算结果
ll solve(ll n)
{
int len=0;
while(n)
{
dig[len++]=n%10;
n/=10;
}
return dfs(len-1,0,0,true).sqr;
}
int main()
{
int T;
ll h,t;
scanf("%d",&T);
init();
while(T--)
{
scanf("%lld%lld",&h,&t);
ll ans=solve(t)-solve(h-1);
/*因为对结果取模,所以有可能后面的solve要比前面小,
负数取模遵循 ans=(ans%mod+mod)%mod */
printf("%lld\n",(ans%mod+mod)%mod);
}
return 0;
}
夸夸勤奋的你,继续加油哦(ノ゚▽゚)ノ