hdu 4507 恨7不成妻(数位dp)

问题

hdu 4507 恨7不成妻 - https://acm.hdu.edu.cn/showproblem.php?pid=4507

分析

参照

  • 详细讨论参照Bomb

状态设计

  • s t a t e 1 ∈ { 0 、 1 、 2 、 3 、 4 、 5 、 6 } state1\in \{0、1、2、3、4、5、6\} state1{0123456}
  • s t a t e 2 ∈ { 0 、 1 、 2 、 3 、 4 、 5 、 6 } state2\in \{0、1、2、3、4、5、6\} state2{0123456}
  • d p [ m ] [ s t a t e 1 ] [ s t a t e 2 ] dp[m][state1][state2] dp[m][state1][state2]:长度为m位的数,且同时满足以下三个条件时的数的个数
    • 各个位上的数字的和模 7 7 7 的余数不等于 s t a t e 1 state1 state1
    • 数模 7 7 7 的余数不等于 s t a t e 2 state2 state2
    • 数不含 7 7 7

计算

  • 例如:求4578以内的与7无关的数的平方和
  • 当最高位取3时, ∑ 1 k ( 3 x y z ) 2 = ∑ 1 k ( 3 × 1 0 3 + x y z ) 2 = ∑ 1 k ( 3 × 1 0 3 ) 2 + 2 × ( 3 × 1 0 3 ) × ∑ 1 k x y z + ∑ 1 k x y z 2 \sum_1^k(3xyz)^2=\sum_1^k(3\times 10^3+xyz)^2=\sum_1^k(3\times 10^3)^2+2\times(3\times 10^3)\times \sum_1^kxyz + \sum_1^kxyz^2 1k(3xyz)2=1k(3×103+xyz)2=1k(3×103)2+2×(3×103)×1kxyz+1kxyz2
  • 上式中, x y z xyz xyz 表示一个3位数, k k k x y z xyz xyz 的个数
  • 显然,需要同时计算"数字的个数"、“数字的和” 及 “数字的平方和”

代码

/* hdu 4507 吉哥系列故事——恨7不成妻 数位dp*/
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MD = 1e9+7;
const int MXN = 25;
struct Ans{ ll x, y, z;}dp[MXN][7][7];
int b[MXN];
ll p[MXN] = {0, 1};
Ans dfs(int m, int r1, int r2, bool ismax){
	if(r1 < 0) r1 = r1+7;
	if(r2 < 0) r2 = r2+7;
	if(m == 0) return (Ans){ r1&&r2, 0, 0};
	if(!ismax && ~dp[m][r1][r2].x) return dp[m][r1][r2];
	ll x = 0, y = 0, z = 0;
	int end = ismax?b[m]:9;	
	for(ll i = 0; i <= end; ++i){
		if(i == 7) continue;
		Ans ans = dfs(m-1, r1-i%7, r2-i*p[m]%7, ismax && i == end);
		ll pm = p[m]%MD;
		x = (x + ans.x)%MD; // 数字个数
		y = (y + pm*i*ans.x%MD + ans.y)%MD; // 数字和
		z = (z + ans.z)%MD; // 数字平方和
		z = (z + pm*i*2%MD*ans.y%MD)%MD;
		z = (z + pm*i*i%MD*pm%MD*ans.x%MD)%MD;
	}
	if(!ismax) dp[m][r1][r2] = (Ans){x, y, z};
	return (Ans){x, y, z};
}
ll solve(ll num){
	int n = 0;
	for(ll i = num; i > 0; i /= 10) b[++n] = i%10;
	return dfs(n, 0, 0, true).z;
}
int main(){
	int t;
	ll L, R;	
	memset(dp, -1, sizeof dp); // 仅初始化一次
	for(int i = 2; i <= 19; ++i) p[i] = 10*p[i-1];
	scanf("%d", &t);
	while(t--){
		scanf("%lld%lld", &L, &R);
		printf("%lld\n", (solve(R)-solve(L-1)+MD)%MD);
	}
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jpphy0

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值