【HDU】4507 吉哥系列故事——恨7不成妻 数位DP

30 篇文章 0 订阅
8 篇文章 0 订阅

题目传送门

挺有意思的一道数位DP题。(本来想用普通的数位DP方法水过,然后就被数据艹了)

这题的解题思路是数位DP,但是需要进行一些加工。

定义f[i][j][k]表示当前已经(从高到低)处理了i位,数位和%7为j,数字%7为k的状态。

状态中记三个值,一个是以当前的数为前缀的合法数字个数c,一个是所有合法数字的数位和sum,一个是所有合法数字的平方和sqr。

考虑状态转移,设答案为ans,之后搜索的状态为k

  • ans.c+=k.c
  • ans.sum+=k.sum+i*pow[x]*k.c,因为我们枚举当前的位置k上放了i,则i在原数中对应i*10^x
  • ans.sqr+=k.sqr+2*k.sum*i*pow[x]+i*pow[x]*i*pow[x]*k.c,这个是根据平方和公式搞出来的,大家可以脑补一下。
综上,我们完成了状态的转移,然后这题就直接记忆化搜索一下就行了。

附上AC代码:

#include <cstdio>
#include <cstring>
using namespace std;

typedef long long ll;
const int p=1e9+7;
struct note{
	ll c,sum,sqr;
}f[20][10][10];
int t,len,a[20];
ll n,m,pow[20];

inline note so(int x,int sum,int num,bool b){
	if (!x) return (note){sum&&num,0,0};
	if (!b&&f[x][sum][num].c!=-1) return f[x][sum][num];
	note k={0},ans={0};
	int lim=b?a[x]:9;
	for (int i=0; i<=lim; ++i)
		if (i!=7){
			k=so(x-1,(sum+i)%7,(num*10+i)%7,b&&i==lim);
			ans.c=(ans.c+k.c)%p;
			ans.sum=((ans.sum+k.sum)%p+i*pow[x]%p*k.c%p)%p;
			ans.sqr=(ans.sqr+(k.sqr+2*pow[x]*i%p*k.sum%p+(i*pow[x])%p*(i*pow[x])%p*k.c%p)%p)%p;
		}
	if (!b) f[x][sum][num]=ans;
	return ans;
}

inline ll work(ll x){
	len=0;
	while (x) a[++len]=x%10,x/=10;
	note k=so(len,0,0,1);
	return k.sqr;
}

int main(void){
	memset(f,-1,sizeof f),pow[1]=1;
	for (int i=2; i<20; ++i) pow[i]=pow[i-1]*10ll%p;
	for (scanf("%d",&t); t; --t) scanf("%lld%lld",&n,&m),printf("%lld\n",(work(m)-work(n-1)+p)%p);
	return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值