HDU 4507 数位dp 超详细按步题解!

题目: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;
}

夸夸勤奋的你,继续加油哦(ノ゚▽゚)ノ

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值