↓ 跳过正文
  1. Posts/

hdu 4507 吉哥系列故事——恨7不成妻 (返回平方和的数位dp)

·1553 字·4 分钟

题目链接 题意:如果一个整数符合下面3个条件之一,那么我们就说这个整数和7有关—— 1、整数中某一位是7; 2、整数的每一位加起来的和是7的整数倍; 3、这个整数是7的整数倍;

现在问题来了:吉哥想知道在一定区间内和7无关的数字的平方和。

思路:如果是求count的话毫无难度,和之前的题目没什么区别,不多说了。

但是是求平方和。一开始我的做法是在dfs中加一个LL的参数表示当前的和,然后每次到dfs的出口,之前统计count的时候返回的是1,表示找到了满足条件的一个数,那这回就返回平方和。但是这样做是错的,具体为什么错没有想得很明白…大概会少算?

然后参考了如下博客: hdu4507解题报告1 hdu4507解题报告2

适牛的博客之数位dp

正解是,之前的dp只统计了一个cnt,这回要同时统计cnt,sum,sqsum(平方和)..

我们先讨论如何求得满足条件的数的和。

我们设某次进入dfs的数是x,当前长度为pos(长度是越来越短的,因为是从高位到低位,对于没有处理到的低位,是按0算的,比如一个五位数xxxxx,第一次dfs以后也许得到4xxxx,x表示没有填的数的位置,实际上这个数就是40000),当前位置要放置的数字是i,考虑其位置,i对这个数的大小(不是和,就是最后要得到的一个数)的贡献是i*10^(pos-1),设为f。那么当前的数就是f+x。我们要求的就是所有f+x的和。现在我们考虑当新添加pos位的数字i对于和的贡献。

当新添加i时,相当于把之前的数整体左移了一位,相当于×10(因为之前[1,x]中的每个满足条件的数都乘了10,所以和也乘了10),然后对于新添加的i,它的贡献是i*cnt[x],含义是对于之前[1,x]所有满足条件的每个数,都进行了pos位置填i的操作,所以对于所有满足条件的和,一共添加了cnt[x]个i。

如果写成用递推的式子就是

*sum[10*x+i] = 10 * sum[x] + cnt[x]i;//感谢@clq学长

如果用dfs的话式子就是

sum[new_state] = Σ{ sum[old_state] + (number to add at the position) * (its base) * count[old_state] }。

维护平方和的方法类似。

(f+x)^2=ff+xx+2fx。

ff直接可以算,xx其实就是上一层dfs得到的(f’+x’)^2嘛,也就是sum2[x](sum2表示平方和)。

2fx也可以算,x就是上一个状态的和。

同样,我们考虑现在已经有了x,处理到pos位置,填到该位置的数字是i的时候对平方和的影响。

xx部分在上一个状态处理过了,即为sum2[x];2f*x也可以通过sum[x]得到。

注意ff,同样,对于之前的[1,x]中每个满足的数,都相当于第pos位添加了i,每个数对平方和的贡献都是ii,所以要*cnt[x]。

以及要不断取模,为了方便写了两个函数来搞,代码看起来清楚一些。。。

哦,还有一个小坑,取模相减以后可能为负数,记得加MOD。

代码实现
  1/* ***********************************************
  2Author :111qqz
  3Created Time :2016年03月16日 星期三 10时44分58秒
  4File Name :code/hdu/4507.cpp
  5 ************************************************ */
  6
  7#include <cstdio>
  8#include <cstring>
  9#include <iostream>
 10#include <algorithm>
 11#include <vector>
 12#include <queue>
 13#include <set>
 14#include <map>
 15#include <string>
 16#include <cmath>
 17#include <cstdlib>
 18#include <ctime>
 19#define fst first
 20#define sec second
 21#define lson l,m,rt<<1
 22#define rson m+1,r,rt<<1|1
 23#define ms(a,x) memset(a,x,sizeof(a))
 24typedef long long LL;
 25#define pi pair < int ,int >
 26#define MP make_pair
 27
 28using namespace std;
 29const double eps = 1E-8;
 30const int dx4[4]={1,0,0,-1};
 31const int dy4[4]={0,-1,1,0};
 32const int inf = 0x3f3f3f3f;
 33const LL MOD = 1E9+7;
 34LL l,r;
 35int digit[30];
 36LL power[40];
 37struct node
 38{
 39    LL cnt,sum,sq;
 40    node(){}
 41    node (LL _cnt,LL _sum,LL _sq):
 42	cnt(_cnt),sum(_sum),sq(_sq){};
 43}dp[25][20][20];
 44LL Add(LL a,LL b)
 45{
 46    LL res = (a+b) %MOD;
 47    return res;
 48}
 49LL Mul(LL a,LL b)
 50{
 51    LL res = ((a%MOD)*(b%MOD))%MOD;
 52    return res;
 53}
 54node dfs (int pos,int mod7,int sum7,bool limit)
 55{
 56    if (pos==0)
 57    {
 58	if (mod7!=0&&sum7!=0) return node(1,0,0);
 59	else return node (0,0,0);
 60    }
 61    if (!limit&&dp[pos][mod7][sum7].cnt!=-1) return dp[pos][mod7][sum7];
 62
 63    int mx = limit?digit[pos]:9;
 64    node res ;
 65    res.cnt=res.sum=res.sq=0;
 66    for ( int i = 0 ; i <= mx;  i++)
 67    {
 68	if (i==7) continue;
 69	node tmp = dfs(pos-1,(mod7+i)%7,(sum7*10+i)%7,limit&&i==mx);
 70	res.cnt = Add(res.cnt,tmp.cnt);
 71	res.sum = Add(res.sum,Add(tmp.sum,Mul(i,Mul(power[pos],tmp.cnt))));
 72	res.sq  = Add(res.sq,Add(tmp.sq,Mul(2*tmp.sum,i*power[pos])));
 73	res.sq  = Add(res.sq,Mul(i*power[pos],Mul(i*power[pos],tmp.cnt)));
 74    }
 75    if (!limit) dp[pos][mod7][sum7] = res;
 76    return res;
 77}
 78LL solve ( LL n)
 79{
 80    int len = 0 ;
 81    ms (digit,0);
 82    while (n)
 83    {
 84	digit[++len] = n % 10;
 85	n/=10;
 86    }
 87    return dfs(len,0,0,true).sq;
 88}
 89
 90void pre()
 91{
 92    power[1] = 1;
 93    for ( int i = 2 ;i  <= 30 ; i++)
 94	power[i] = (power[i-1]*10LL)%MOD;
 95}
 96int main()
 97{
 98#ifndef  ONLINE_JUDGE
 99    freopen("code/in.txt","r",stdin);
100#endif
101    pre();
102    int T;
103    cin>>T;
104    ms(dp,-1);
105    while (T--)
106    {
107	scanf("%lld %lld",&l,&r);
108	LL ans = solve (r) - solve (l-1);
109	while (ans<0) ans +=MOD;
110	//  ans = ans % MOD;
111	printf("%lld\n",ans);
112    }
113#ifndef ONLINE_JUDGE
114    fclose(stdin);
115#endif
116    return 0;
117}

相关文章

hdu 4722 good numbers (带整除的数位dp)

·478 字·1 分钟
题目链接 题意:求一个区间内所有位数字之和能被10整除的数的个数。 思路:数位dp,dfs要一个参数记录从最高位到现在的pos位置的数字之和的结果。 代码实现 1 dp[i][j] 表示长度为i,和为j的方案数。 2 记得开long long ,然而我开了那么多long long 忘了dp 的long long 结果wa到死。。果然大早上不清醒吗== 3 4 5 6/* *********************************************** 7Author :111qqz 8Created Time :2016年03月16日 星期三 08时10分19秒 9File Name :code/hdu/4722.cpp 10************************************************ */ 11 12#include <cstdio> 13#include <cstring> 14#include <iostream> 15#include <algorithm> 16#include <vector> 17#include <queue> 18#include <set> 19#include <map> 20#include <string> 21#include <cmath> 22#include <cstdlib> 23#include <ctime> 24#define fst first 25#define sec second 26#define lson l,m,rt<<1 27#define rson m+1,r,rt<<1|1 28#define ms(a,x) memset(a,x,sizeof(a)) 29typedef long long LL; 30#define pi pair < int ,int > 31#define MP make_pair 32 33using namespace std; 34const double eps = 1E-8; 35const int dx4[4]={1,0,0,-1}; 36const int dy4[4]={0,-1,1,0}; 37const int inf = 0x3f3f3f3f; 38LL l,r; 39int digit[30]; 40LL dp[30][15]; //dp 数组忘记开long long ,wa到死。。。。。。。。。日了哈士奇。 41LL dfs ( int pos,int sum,bool limit) 42{ 43 if (pos==0) 44 { 45 if (sum==0) return 1; 46 else return 0; 47 } 48 if (!limit&&dp[pos][sum]!=-1) return dp[pos][sum]; 49 50 int mx = limit?digit[pos]:9; 51 52 LL res = 0 ; 53 for ( int i = 0 ; i <= mx; i ++) 54 { 55 res+=dfs(pos-1,(sum+i),limit&&i==mx); 56 } 57 58 if (!limit) dp[pos][sum] = res; 59 60 return res; 61 62} 63LL solve ( LL n) 64{ 65// if (n==0) return 1; 66 // if (n<=9) return 0; 67 if (n<0) return 0; 68 ms(digit,0); 69 int len = 0 ; 70 while (n) 71 { 72 digit[++len] = n % 10; 73 n /= 10; 74 } 75 76 return dfs(len,0,true); 77} 78int main() 79{ 80 #ifndef ONLINE_JUDGE 81 freopen("code/in.txt","r",stdin); 82 #endif 83// ios::sync_with_stdio(false); 84 int T; 85 cin>>T; 86 ms(dp,-1); 87 int cas = 0 ; 88 while (T--) 89 { 90 scanf("%lld %lld",&l,&r); 91 LL ans = solve (r)-solve(l-1); 92 93 printf("Case #%d: %lld\n",++cas,ans); 94 } 95 96 #ifndef ONLINE_JUDGE 97 fclose(stdin); 98 #endif 99 return 0; 100}

bzoj 1026 windy数(数位dp入门题)

·757 字·2 分钟
题目链接 题意:不含前导零且相邻两个数字之差至少为2的正整数被称为windy数。 windy想知道,在A和B之间,包括A和B,总共有多少个windy数? 思路:数位dp 这道题的特点是前面不允许前导0,也就是说,如果第i位前面全是0的话,这个数就变成了i位数,i就变成了最高位,而最高位没有前面的数(**如果这里不考虑不允许前导0这个因素而把前面的一个数认为成是0就错了) **最高位的数可以直接取。 还有记忆化调用以及存储的时候也要注意…只有当位数相同的时候转移才有意义。 具体的方法是dfs中多了一个prehasnonzero的bool变量,就是字面意思,判断当前位置前面的位置是够存在一个非0的值。

hdu 3555 Bomb (数位dp入门题)

·401 字·1 分钟
题目链接 题意:问从1到n的所有数中,有多少个数含有数字串“49” 思路:和上一道不要62很像,但是由于是要统计有49的,但是有49的情况实在太多了,正难则反,用减法定理反过来考虑,先统计出不含49的数的个数,这样就和不要62一样了,然后再用总数减。