现在的位置: 首页 > 综合 > 正文

数位dp

2018年12月19日 ⁄ 综合 ⁄ 共 19585字 ⁄ 字号 评论关闭

点击打开链接

点击打开链接

什么是数位 DP?

在信息学竞赛中,有一类难度不大但异常麻烦的问题——数位计数问题,这类问题的主要特点是询问的答案和一段连续的数的各个数位相关,并且需要对时间效率有一定要求。由  
于解决这类问题往往意味着巨大的代码量,而众多的特殊情况又意味着出现错误的巨大可能性,因此很少有人愿意解决此类问题,但只要掌握好的方法,解决这类问题也并非想象中的那样困难。   
——《数位计数问题解法研究》   高逸涵 

在信息学竞赛中,有这样一类问题:求给定区间中,满足给定条件的某个D 进制数或此类数的数量。所求的限定条件往往与数位有关,例如数位之和、指定数码个数、数的大小顺序分组等等。题目给定的区间往往很大,无法采用朴素的方法求解。此时,我们就需要利用数位的性质,设计log(n)级别复杂度的算法。解决这类问题最基本的思想就是“逐位确定”的方法。下面就让我们通过几道例题来具体了解一下这类问题及其思考方法。  
——《浅谈数位类统计问题》   刘聪

数位DP的解题思路
1.对于一个数,若其首位已经比询问上界小,则剩余位没有任何限制。此时如果能直接处理这一情况,则问题距离解决又会迈出一大步。   
2.例如,在十进制下,计算[10000,54321]内的数字和,我们可以将其分解为:   
3.[10000,19999],[20000,29999],[30000,39999],[40000,49999],[50000,54321]。   
4.前四个区间如果可以直接解决,则只需处理最后一个区间,进一步将最后一个区间划分  
5.为:[50000,50999],[51000,51999],[52000,52999],[53000,53999],[54000,54321]。同理将最后一  
6.个区间划分下去,最后可以得到以下区间划分:   
7.[10000,19999],[20000,29999],[30000,39999],[40000,49999],   
8.[50000,50999],[51000,51999],[52000,52999],[53000,53999],   
9.[54000,54099],[54100,54199],[54200,54299],   
10.[54300,54309],[54310,54319],   
11.[54320,54321]   

数位DP的经典模板(记忆化搜索)

int dfs(int i, int s, bool e){
    if(i==-1) return s = target_s;
    if(!e && ~f[i][s]) return f[i][s];
    int res = 0;
    int  u = e?num[i]:9;
    for(int d = first?1:0; d<=u; ++d)
        res += dfs(i-1, new_s(s, d), e&&d==u);
    return e?res:f[i][s] = res;
}
f为记忆化数组;
i为当前处理串的第i位(权重表示法,也即后面剩下i+1位待填数);
s为之前数字的状态(如果要求后面的数满足什么状态,也可以再记一个目标状态t之类,for的时候枚举下t);
e表示之前的数是否是上界的前缀(即后面的数能否任意填)。
for循环枚举数字时,要注意是否能枚举0,以及0对于状态的影响,有的题目前导0和中间的0是等价的,但有的不是,对于后者可以在dfs时再加一个状态变量z,
表示前面是否全部是前导0,也可以看是否是首位,然后外面统计时候枚举一下位数。It depends.

于是关键就在怎么设计状态。当然做多了之后状态一眼就可以瞄出来。

 //==============================================================================
经典题目

Hdu 2089 不要62
求给定区间中不含有62和4的数的个数。
///s=
///0 表示不含“62” ,“4”
///1 表示前一位为"6"
int dfs(int i, int s, int e) {
    if(i==-1) return 1;
    if(!e && ~dp[i][s]) return dp[i][s];
    int res = 0;
    int u = e?num[i]:9;
    for(int d=0; d<=u; ++d) {
        if(d==4) continue;
        if(s&&(d==2)) continue;
        res += dfs(i-1, d==6, e&&d==u);
    }
    return e?res:dp[i][s] = res;
}

Hdu 3555 Bomb
求给定区间求含有49的数的个数。
/*
s=
0 不含 “49”
1 前一位为“4”
2 含“49”
*/
inline int new_s(int s, int d) {
    if(s==2 ||(s==1&&d==9)) {
        return 2;
    } else if(d==4) {
        return 1;
    } else
        return 0;
}
LL dfs(int i, int s, int e) {
    if(i==-1) return s==2;
    if(!e && ~dp[i][s]) return dp[i][s];
    LL res = 0;
    int u = e?num[i]:9;
    for(int d=0; d<=u; ++d) {
        res += dfs(i-1, new_s(s,d), e&&d==u);
    }
    return e?res:dp[i][s]=res;
}

HYSBZ - 1026 windy数
求给定区间范围内的,求相邻数位之差绝对值不小于2的数的个数。
//s==0,表示有前缀零
int dfs(int i, int s, int e, int pre) {
    if(i==-1) return s==1;
    if(!e&&~dp[i][pre]&s) return dp[i][pre];
    int res = 0;
    int u = e?num[i]:9;
    for(int d=0; d<=u; ++d)
        if(!s||abs(pre-d)>=2)
            res += dfs(i-1, s||d, e&&(d==u), d);

    if (!e&&s) dp[i][pre] = res;
    return res;
}
dfs(cnt-1, 0, 1, 11);

Hdu 3709 Balanced Number
平衡数。数n以数n中的某个位为支点,每个位上的数权值为(数字xi*(posi - 支点的posi)),如果数n里有一个支点使得所有数权值之和为0那么她就是平衡数。比如4139,以3为支点,左边 = 4 * (4 - 2) + 1 * (3 - 2) = 9,右边 = 9 * (1 - 2) = -9,左边加右边为0,所以4139是平衡数。现在给出一个区间[l,r],问区间内平衡数有多少个?
[cpp] view plaincopy
1.LL dfs(int pos,int o,int pre,int limit){  
2.    LL res=0;  
3.    if (pos<0)    return pre==0;  
4.    if (!limit&&f[pos][o][pre]!=-1) return f[pos][o][pre];  
5.    int last=limit?dig[pos]:9;  
6.    for (int i=0;i<=last;i++){  
7.        res+=dfs(pos-1,o,pre+i*(pos-o),limit&&(i==last));  
8.    }  
9.    if (!limit) f[pos][o][pre]=res;  
10.    return res;  
11.}  
12.  
13.LL solve(LL n){  
14.    if (n<0) return 0;  
15.    if (n==0) return 1;  
16.    int len=0;  
17.    while (n){  
18.        dig[len++]=n%10;  
19.        n/=10;  
20.    }  
21.    LL ans=0;  
22.    for (int i=0;i<len;i++){  
23.        ans+=dfs(len-1,i,0,1);  
24.    }  
25.    ans=ans-len+1;  
26.    return ans;  
27.}  

CodeForces 55D Beautiful numbers
如果一个数能够被其每个数位的数都整除,那么这个数就叫做美丽数。
[cpp] view plaincopy
1.int check(int bit,int mod){  
2.    for (int i=2;i<=9;i++){  
3.        if (bit&(1<<(i-2))){  
4.            if (mod%i) return 0;  
5.        }  
6.    }  
7.    return 1;  
8.}  
9.  
10.LL dfs(int pos,int bit,int mod,int limit){  
11.    if (pos<0) return check(bit,mod);  
12.    if (!limit && f[pos][bit][mod]!=-1) return f[pos][bit][mod];  
13.    int last=limit?dig[pos]:9;  
14.    LL ret=0;  
15.    for (int i=0;i<=last;i++){  
16.        int nbit=bit;  
17.        if (i>=2) nbit|=1<<(i-2);  
18.        ret+=dfs(pos-1,nbit,(mod*10+i)%MOD,limit&&(i==last));  
19.    }  
20.    if (!limit) f[pos][bit][mod]=ret;  
21.    return ret;  
22.}  

Hdu 3652 B-number
求小于n是13的倍数且含有'13'的数的个数。
[cpp] view plaincopy
1.LL dfs(int pos,int pre,int fg,int md,int limit){  
2.    if (pos<0) return fg&&(md==0);  
3.    if (!limit&&f[pos][pre][fg][md]!=-1) return f[pos][pre][fg][md];  
4.    LL res=0;  
5.    int last=limit?dig[pos]:9;  
6.    for (int i=0;i<=last;i++){  
7.        res+=dfs(pos-1,i,fg||(pre==1&&i==3),(md*10+i)%13,limit&&(i==last));  
8.    }  
9.    if (!limit) f[pos][pre][fg][md]=res;  
10.    return res;  
11.}  

Hdu 4352 XHXJ's LIS
求[L,R]内最长递增子序列是k的数的个数;
[cpp] view plaincopy
1.LL f[maxn][1<<10][11];  
2.int K;  
3.int dig[maxn];  
4.int getLIS(int bit){  
5.    int res=0;  
6.    for (int i=0;i<10;i++){  
7.        if (bit&(1<<i)) res++;  
8.    }  
9.    return res;  
10.}  
11.int gaoBit(int bit,int d){  
12.    if (bit&(1<<d)) return bit;  
13.    if ((1<<d)>bit) return bit|(1<<d);  
14.    bit|=(1<<d);  
15.    for (int i=d+1;i<10;i++){  
16.        if (bit&(1<<i)) return bit^(1<<i);  
17.    }  
18.    return 0;  
19.}  
20.  
21.LL dfs(int pos,int bit,int limit){  
22.    if (pos<0) return getLIS(bit)==K;  
23.    if (!limit&&f[pos][bit][K]!=-1) return f[pos][bit][K];  
24.    LL res=0;  
25.    int last=limit?dig[pos]:9;  
26.    for (int i=0;i<=last;i++){  
27.  
28.        int go;  
29.        if (bit==0&&i==0) go=0;  
30.        else go=gaoBit(bit,i);  
31.        res+=dfs(pos-1,go,limit&&(last==i));  
32.    }  
33.    if (!limit) f[pos][bit][K]=res;  
34.    return res;  
35.}  
36.  
37.LL solve(LL n){  
38.    int len=0;  
39.    while (n){  
40.        dig[len++]=n%10;  
41.        n/=10;  
42.    }  
43.    return dfs(len-1,0,1);  
44.}  

HDU 4507 吉哥系列故事——恨7不成妻
中文题。需要推公式。
[cpp] view plaincopy
1.#include <iostream>  
2.#include <cstring>  
3.  
4.using namespace std;  
5.typedef long long LL;  
6.const int maxn=22;  
7.const int MOD=1e9+7;  
8.  
9.  
10.typedef pair<LL,LL> PII;  
11.typedef pair<PII,LL> PIII;  
12.  
13.inline LL fst(PIII a){  
14.    return a.first.first;  
15.}  
16.inline LL sec(PIII a){  
17.    return a.first.second;  
18.}  
19.inline LL thd(PIII a){  
20.    return a.second;  
21.}  
22.inline PIII makeZeroPIII(){  
23.    return make_pair(make_pair(0,0),0);  
24.}  
25.inline PIII makeOnePIII(){  
26.    return make_pair(make_pair(1,0),0);  
27.}  
28.LL power[maxn];  
29.PIII f[maxn][11][8];  
30.bool v[maxn][11][8];  
31.int dig[maxn];  
32.  
33.PIII gao(PIII a, PIII b,int i,int pos){  
34.    PIII res=makeZeroPIII();  
35.    res.first.first=(fst(a)+fst(b))%MOD;//数量  
36.    res.first.second=(sec(a)+sec(b)+((i*power[pos])%MOD*fst(b))%MOD)%MOD;//和  
37.    res.second=(thd(a)+thd(b)+((2*i*power[pos])%MOD*sec(b))%MOD+(((i*i*power[pos])%MOD*power[pos])%MOD*fst(b))%MOD)%MOD;//平方和  
38.    return res;  
39.}  
40.  
41.PIII dfs(int pos,int pre,int sev,int limit){  
42.    if (pos<0) {  
43.        if (pre!=0&&sev!=0) return makeOnePIII();  
44.        else return makeZeroPIII();  
45.    }  
46.    if (!limit&&v[pos][pre][sev]) return f[pos][pre][sev];  
47.    PIII res=makeZeroPIII();  
48.    int last=limit?dig[pos]:9;  
49.    for (int i=0;i<=last;i++){  
50.        if (i==7) continue;  
51.        PIII tmp=dfs(pos-1,(pre*10+i)%7,(sev+i)%7,limit&&(i==last));  
52.        res=gao(res,tmp,i,pos);  
53.    }  
54.    if (!limit){  
55.        v[pos][pre][sev]=true;  
56.        f[pos][pre][sev]=res;  
57.    }  
58.    return res;  
59.}  
60.  
61.LL solve(LL n){  
62.    int len=0;  
63.    while (n){  
64.        dig[len++]=n%10;  
65.        n/=10;  
66.    }  
67.    PIII ans = dfs(len-1,0,0,1);  
68.    return thd(ans);  
69.}  
70.  
71.int main()  
72.{  
73.    memset(v,0,sizeof(v));  
74.    memset(power,0,sizeof(power));  
75.    power[0]=1;  
76.    for (int i=1;i<maxn;i++){  
77.        power[i]=(power[i-1]*10)%MOD;  
78.    }  
79.    int T;  
80.    cin>>T;  
81.    while (T--){  
82.        LL a,b;  
83.        cin>>a>>b;  
84.        cout<<(solve(b)-solve(a-1)+MOD)%MOD<<endl;  
85.    }  
86.    return 0;  
87.}  

POJ 3252 round number
统计一个区间内有多少个round number数?
"round number"定义:二进制形式中"0"的个数不小于"1"的个数。

//preZero=true表示当前位前面全为零
int dfs(int i, int sum0, int sum1, bool e, bool preZero) {
    if(i==-1) return sum0>=sum1;
    if(!e && ~dp[i][sum0][sum1]) return dp[i][sum0][sum1];
    int res = 0;
    int u = e?num[i]:1;
    for(int d=0; d<=u; ++d) {
        int t0 = sum0, t1 = sum1;
        if(preZero || d) {
            t0 +=(d==0);
            t1 +=(d==1);
        }
        res += dfs(i-1, t0, t1, e&&(d==u), preZero||d);
    }
    return e?res:dp[i][sum0][sum1]=res;
}

Step2:撸中等题
1.hdu 4507

http://acm.hdu.edu.cn/showproblem.php?pid=4507

Tecent 2012.3.21 C 题
题目描述
求[l , r] 中
 如果一个整数符合下面3个条件之一,那么我们就说这个整数和7有关——
  1、整数中某一位是7;
  2、整数的每一位加起来的和是7的整数倍;
  3、这个整数是7的整数倍;

  现在问题来了:吉哥想知道在一定区间内和7无关的数字的平方和。
方法:
肯定是数位dp
必须这个满足减法——[l , r] = [0 , r] - [0 , l - 1]
我的算法是算相反的——即和数7有关的平方和,然后用1/6 * n * (n + 1) * (2n + 1) 来减一下

首先确定状态 —— dp[枚举位数][是否含有7][个数的和%7][这个数%7]
其次是维护:
1、个数
2、和 (由个数算出
3、平方和(由个数和 和 算出
首先说个数算法:
这就是一个最裸的数位dp,贴个模板就能做了 。。模板含义见资料
再说和的做法:(这里很容易错啊。。。)
在枚举第 i 位 是 d 的时候,算出 i - 1 位之后的个数 has ,那么这个d 就出现了 has 次,于是就要统计 d * 10的幂 * has 。 
最后说平方和的算法:(卧槽这里写挂了好久好么。。。)
还是枚举第 i 位 是 d 的时候。我们看跟后面的关系。假设这个数是 dxxxxxxx(后面一大堆数字) 。假设这个数字是 d * 10的幂(设为 x) + y(后面一大堆数字),那么我们就是要计算(x + y)^2。 拆开来就是 x ^ 2 + 2xy + y ^ 2 。 首先 y ^ 2 在后面我们已经算过了,直接 搜索深度+1就可以计算,2xy 的话需要用 2 * x * (出现次数) * (后面数字的一次幂和), x ^ 2 的话直接 就  x ^ 2 乘以 后面的出现次数。
那么这个题目就解决了。。。

Debug方法:
卧槽数位dp debug 很关键好么。。
我这次的方法很科学,用个暴力的代码算出来比较坑爹的几个数字,比如 7 , 14 , 21 , 100 , 139 的三个部分 —— 个数 , 和, 平方和
然后,剥洋葱皮一样一点儿一点儿展开,先debug最外层的——个数,过掉几个数据之后说明可以信任;然后搞 和最后搞 平方和

Code
我的Code
[cpp] view plaincopyprint?
1.LL cnt[20][2][7][7] , dp[20][2][7][7] , dpsum[20][2][7][7];  
2.LL ten[20];  
3.LL num[20];  
4.void init(){  
5.    ten[0] = 1;  
6.    REP_1(i , 19) ten[i] = (ten[i - 1] * 10) % MOD;  
7.}  
8.LL dfscnt(int i, bool seven , int numbersum, int sum ,  bool e) {  
9.    if (i==-1) return (seven || numbersum % 7 == 0 || sum % 7 == 0) ? 1 : 0;  
10.    if (!e && ~cnt[i][seven][numbersum][sum]) return cnt[i][seven][numbersum][sum];  
11.    LL res = 0;  
12.    int u = e?num[i]:9;  
13.    for (int d = 0; d <= u; ++d)  
14.        res = ( res + dfscnt(i-1, (seven || d == 7), (numbersum + d) % 7 , (sum * 10 + d) % 7 ,  e&&d==u)) % MOD;  
15.    return e?res:cnt[i][seven][numbersum][sum]=res;  
16.}  
17.LL mul(LL x , LL y){  
18.    x %= MOD;  
19.    y %= MOD;  
20.    return (x * y) % MOD;  
21.}  
22.LL dfssum(int i , int seven , int numbersum , int sum , bool e){  
23.    if (i == -1) return 0;  
24.    if (!e && ~dpsum[i][seven][numbersum][sum]) return dpsum[i][seven][numbersum][sum];  
25.    LL res = 0;  
26.    int u = e ? num[i] : 9;  
27.    for (int d = 0 ; d <= u ; ++d){  
28.        LL has = dfscnt(i-1, (seven || d == 7), (numbersum + d) % 7 , (sum * 10 + d) % 7 ,  e&&d==u);  
29.        LL tmp = mul(d , ten[i]);  
30.        tmp = mul(has , tmp);  
31.        res = (res + dfssum(i-1, (seven || d == 7), (numbersum + d) % 7 , (sum * 10 + d) % 7 ,  e&&d==u)) % MOD;  
32.        res = (res + tmp) % MOD;  
33.    }  
34.    return e ? res : dpsum[i][seven][numbersum][sum] = res ;  
35.}  
36.LL dfs(int i , int seven , int numbersum , int sum , bool e){  
37.    if (i == -1) return 0;  
38.    if (!e && ~dp[i][seven][numbersum][sum]) return dp[i][seven][numbersum][sum];  
39.    LL res = 0;  
40.    int u = e ? num[i] : 9;  
41.    for (int d = 0 ; d <= u ; ++d){  
42.        LL has = dfscnt(i-1, (seven || d == 7), (numbersum + d) % 7 , (sum * 10 + d) % 7 ,  e&&d==u);  
43.        LL sum1 = dfssum(i-1, (seven || d == 7), (numbersum + d) % 7 , (sum * 10 + d) % 7 , e&&d==u);  
44.        LL tmp = mul(d , ten[i]);  
45.        LL nownow = mul(tmp , tmp);  
46.        LL hasnow = mul(nownow , has);  
47.        nownow = mul(2 , mul(tmp , sum1));  
48.        res = (res + dfs(i-1, (seven || d == 7), (numbersum + d) % 7 , (sum * 10 + d) % 7 ,   e&&d==u)) % MOD;  
49.        res = (res + hasnow) % MOD;  
50.        res = (res + nownow) % MOD;  
51.    }  
52.    return e ? res : dp[i][seven][numbersum][sum] = res ;  
53.}  
54.LL l , r;  
55.LL gao(LL x){  
56.    LL a = x , b = x + 1 , c = 2 * x + 1;  
57.    LL p = 2;  
58.    if (a % p == 0) a /= p;  
59.    else if (b % p == 0)  b /= p;  
60.    else if (c % p == 0) c /= p;  
61.    p = 3;  
62.    if (a % p == 0) a /= p;  
63.    else if (b % p == 0)  b /= p;  
64.    else if (c % p == 0) c /= p;  
65.    return mul(mul(a , b) , c);  
66.}  
67.LL getans(LL x){  
68.    if (x == 0) return 0;  
69.    int s = 0;  
70.    LL y = x;  
71.    while(x){  
72.        num[s ++ ] = x % 10;  
73.        x /= 10;  
74.    }  
75.    x = y;  
76.    LL res = gao(x);  
77.    res -= dfs(s - 1 , 0 , 0 , 0 , 1);  
78.    res %= MOD;  
79.    res += MOD;  
80.    res %= MOD;  
81.    return res;  
82.}  
83.void solve(){  
84.    RD(l , r);  
85.    LL ans = getans(r) - getans(l - 1);  
86.    ans %= MOD;  
87.    ans += MOD;  
88.    ans %= MOD;  
89.    printf("%I64d\n" , ans);  
90.}  
91.int main(){  
92.    FLC(dp , -1);  
93.    FLC(cnt , -1);  
94.    FLC(dpsum , -1);  
95.    init();  
96.    Rush solve();  
97.}  

AC的(不是很懂。。。)
[cpp] view plaincopyprint?
1.#include<cstdio>  
2.#include<cstring>  
3.#include<algorithm>  
4.#include<cmath>  
5.#include<vector>  
6.#include<map>  
7.#include<set>  
8.#include<queue>  
9.#include<string>  
10.#include<iostream>  
11.using namespace std;  
12.  
13.typedef long long LL;  
14.typedef double db;  
15.#define mp make_pair  
16.#define pb push_back  
17.  
18.const LL P = (int)1e9 + 7;  
19.LL f[20][9 * 20][7][2];//cnt  
20.LL sum1[20][9 * 20][7][2];// sum = 1次和  
21.LL sum2[20][9 * 20][7][2];//sum = 2次和  
22.LL ten[100];  
23.  
24.void update(LL &a, LL b) {  
25.    a = (a + b) % P ;  
26.    if(a<0) a+=P;  
27.}  
28.  
29.LL MOD(LL a) {  
30.    a %= P;  
31.    return a <0 ? a + P : a;  
32.}  
33.  
34./*debug*/  
35.LL _f[20][9 * 20][7][2];  
36.LL _sum1[20][9 * 20][7][2];  
37.LL _sum2[20][9 * 20][7][2];  
38.  
39.void _pre(){  
40.    _f[0][0][0][0]= 1;  
41.      
42.    for(LL i = 1;i<=6;++i) {  
43.          
44.        LL lo = 0, hi = ten[i]-1;  
45.        for(LL v = lo; v <= hi; ++ v){  
46.            LL rem = v % 7, dsum = 0;  
47.            LL k = v,msk = 0;  
48.            while(k){  
49.                LL d = k%10;  
50.                if(d == 7) msk = 1;  
51.                dsum += d;k/=10;  
52.            }  
53.            _f[i][dsum][rem][msk]++;  
54.            update(_sum1[i][dsum][rem][msk] , v);  
55.            update(_sum2[i][dsum][rem][msk] , (LL)v*v%P);  
56.        }  
57.    }  
58.    for(LL i = 0; i <= 6; ++ i) {  
59.        LL sumlim = i * 9;  
60.  
61.        for(LL sum = 0; sum <= sumlim; ++ sum) {  
62.            for(LL modres = 0; modres < 7; ++ modres) {  
63.                for(LL has7 = 0; has7 < 2; ++ has7) {  
64.                    if(_f[i][sum][modres][has7] != f[i][sum][modres][has7]) {  
65.                        cout << "error at f!" << endl;  
66.                        return;  
67.                    }  
68.                    if(_sum1[i][sum][modres][has7] != sum1[i][sum][modres][has7]) {  
69.                        cout << "error at s1!" << endl;  
70.                        cout << i <<" " << sum <<" " << modres << " " << has7 << endl;  
71.                        cout <<_sum1[i][sum][modres][has7] <<"  "<<sum1[i][sum][modres][has7]<<endl;  
72.                        return;  
73.                    }  
74.                    if(_sum2[i][sum][modres][has7] != sum2[i][sum][modres][has7]) {  
75.                        cout << "error at s2!" << endl;  
76.                        cout <<_sum2[i][sum][modres][has7] <<"  "<<sum2[i][sum][modres][has7]<<endl;  
77.                        return;  
78.                    }  
79.                }  
80.            }  
81.        }  
82.    }  
83.    cout <<"ok"<<endl;  
84.}  
85.  
86.LL ten7[100];  
87.  
88.void pre(){  
89.    f[0][0][0][0] = 1;  
90.    ten[0] = 1; ten7[0]=1;  
91.    for(LL i = 1; i <= 99; ++ i) ten[i]=ten[i-1] * 10%P;  
92.    for(LL i = 1; i <= 99; ++ i) ten7[i]=ten7[i-1] * 10%7;  
93.      
94.    for(LL i = 1; i <= 19; ++ i) {  
95.        for(LL j = 0; j < 10; ++ j) {  
96.            LL sumlim = (i-1) * 9;  
97.              
98.            for(LL sum = 0; sum <= sumlim; ++ sum) {  
99.                for(LL modres = 0; modres < 7; ++ modres) {  
100.                    for(LL has7 = 0; has7 < 2; ++ has7) {  
101.                        LL nbit = i, nsum = sum + j, nmod = (modres+ten7[i-1]*j%7)%7,nhas  
102.                            = has7 | (j == 7);  
103.                        if( nsum <= sumlim + 9) {  
104.                            update(f[nbit][nsum][nmod][nhas],  
105.                                f[i-1][sum][modres][has7]);  
106.                        }  
107.                    }  
108.                }  
109.            }  
110.        }  
111.    }  
112.      
113.    // sum1  
114.    for(LL i = 1; i <= 19; ++ i) {  
115.        for(LL j = 0; j < 10; ++ j) {  
116.            LL sumlim = (i-1) * 9;  
117.  
118.            for(LL sum = 0; sum <= sumlim; ++ sum) {  
119.                for(LL modres = 0; modres < 7; ++ modres) {  
120.                    for(LL has7 = 0; has7 < 2; ++ has7) {  
121.                        LL nbit = i, nsum = sum + j, nmod = (modres+ten7[i-1]*j%7)%7,nhas  
122.                            = has7 | (j == 7);  
123.                        if( nsum <= sumlim + 9) {  
124.                            update(sum1[nbit][nsum][nmod][nhas],  
125.                                MOD( sum1[i-1][sum][modres][has7]+((ten[i-1]*j)%P)*f[i-1][sum][modres][has7]%P ) );  
126.                        }  
127.                    }  
128.                }  
129.            }  
130.        }  
131.    }  
132.      
133.    // sum2  
134.    for(LL i = 1; i <= 19; ++ i) {  
135.        for(LL j = 0; j < 10; ++ j) {  
136.            LL sumlim = (i-1) * 9;  
137.  
138.            for(LL sum = 0; sum <= sumlim; ++ sum) {  
139.                for(LL modres = 0; modres < 7; ++ modres) {  
140.                    for(LL has7 = 0; has7 < 2; ++ has7) {  
141.                        LL nbit = i, nsum = sum + j, nmod = (modres+ten7[i-1]*j%7)%7,nhas  
142.                            = has7 | (j == 7);  
143.                        if( nsum <= sumlim + 9) {  
144.                            LL tmp = 0, cnt = f[i-1][sum][modres][has7];  
145.                            tmp = MOD(tmp + sum2[i-1][sum][modres][has7]);  
146.                            tmp = MOD(tmp + (ten[2*i-2]*j*j)%P*cnt%P);  
147.                            tmp = MOD(tmp + (ten[i-1]*j*2)%P*sum1[i-1][sum][modres][has7]%P);  
148.                            update(sum2[nbit][nsum][nmod][nhas],  
149.                                tmp );  
150.                        }  
151.                    }  
152.                }  
153.            }  
154.        }  
155.    }  
156.}  
157.  
158.bool ok(LL x) {  
159.    LL dsum = 0;  
160.    if(x % 7 == 0) return true;  
161.    while(x) {  
162.        LL d = x % 10;  
163.        if(d == 7) return true;  
164.        dsum += d; x/=10;  
165.    }  
166.    return dsum %7 == 0;  
167.}  
168.  
169.LL mul(LL a, LL b, LL c) {  
170.    a %= c;  
171.    b %= c;  
172.    return a*b % c;  
173.}  
174.  
175.LL gao(LL x) {  
176.    if(x <= 0) return 0;  
177.    LL ans = ok(x)?0:mul(x,x,P);  
178.    //cout << x <<" ans = " << ans << endl;  
179.    LL has7 = 0;  
180.    LL sum = 0;  
181.    LL modres = 0;  
182.    LL pre = 0;  
183.      
184.    LL d[22],dlen=0;  
185.    while(x) d[dlen++]=x%10,x/=10;  
186.      
187.    for(LL i = dlen-1,j=0;i>=0;--i,++j) {  
188.        for(LL dg = 0; dg < d[i]; ++ dg) {  
189.            for(LL dsum = 0; dsum <= i*9; ++ dsum) {  
190.                for(LL md = 0; md < 7; ++ md) {  
191.                    for(LL msk = 0; msk < 2; ++ msk) {  
192.                          
193.                        LL mdsum = dsum + dg + sum;  
194.                          
195.                        LL tmp = (modres*10 + dg)%7;  
196.                          
197.                        LL mmd = (ten7[i]*tmp+md)%7;  
198.                          
199.                        LL mmsk = has7 | msk | (dg == 7);  
200.                          
201.                        if( mmsk == 1 || mdsum % 7 ==0 || mmd == 0) continue;  
202.  
203.                        LL dd = (pre * 10 + dg) % P;  
204.                        LL cct = f[i][dsum][md][msk];  
205.                          
206.                        update(ans, sum2[i][dsum][md][msk]);  
207.                        update(ans, MOD(((dd*2%P)*ten[i])%P*sum1[i][dsum][md][msk]%P));  
208.                        update(ans, ((((dd*dd%P)*ten[i])%P*ten[i])%P*cct)%P);  
209.                    }  
210.                }  
211.            }  
212.        }  
213.          
214.        if(d[i] == 7) has7 = 1;  
215.        sum += d[i];  
216.        modres = (modres*10+d[i])%7;  
217.        pre = pre * 10 + d[i];  
218.    }  
219.    return ans;  
220.}  
221.  
222.int main(){  
223.    pre();  
224.    LL T;  
225.    cin >> T;  
226.    while(T --) {  
227.        LL L, R; cin >> L >> R;  
228.        cout << MOD(  gao(R)-gao(L-1) ) <<endl;  
229.    /* 
230.        LL ans = 0; 
231.        for(LL x = L; x <= R; ++ x) if(!ok(x)) 
232.            update(ans, mul(x,x,P)); 
233.            cout << ans << endl;*/  
234.    }  
235.    return 0;  
236.}  

2.SGU 258
problem
一个2*n位数,前n位数各位数和与后n位数各位数和相等,是lucky数。一个2*n位数,改变一个数字后,依然是2*n位(改前改后都没有前导零),并且是lucky数,则改之前的数称之为近似lucky数。求[l, r] 区间内,有多少近似lucky数。

think
dp[枚举到那一位][这个数是2*n位数][sum(前n位各位数和-后n位数各位数和)][more(最多增加)][less(最多减少)]
sum可能<0 所以给他都+45
more = max(9-前n位某个数, 后n位某个数)
less = max(首位-1, 前n位且非首位的某个数, 9-后n位某个数) //因为首位不能变成0 所以首位-1
答案是sum!=45(是0 的话就不用变了) && sum+more >=45 && sum-less<=45 

code
[cpp] view plaincopyprint?
1.const int M = 45;  
2.int f[10][10][91][10][10];  
3.int b[11];  
4.  
5.int dfs(int pos, int N, int sum, int more, int less, int e){  
6.    if(pos==0){  
7.        return sum!=M && sum+more>=M && sum-less<=M;  
8.    }  
9.    if(!e && ~f[pos][N][sum][more][less]) return f[pos][N][sum][more][less];  
10.    int ans = 0;  
11.    int u = e?b[pos]:9;  
12.    int d = pos==N?1:0;  
13.    for(; d<=u; d++){  
14.        int ss = pos>N/2 ? sum+d : sum-d;  
15.        int mm = pos>N/2 ? max(more, 9-d) : max(more, d);  
16.        int ll = pos>N/2 ? max(less, pos==N?d-1:d) : max(less, 9-d);  
17.        ans += dfs(pos-1, N, ss, mm, ll, e&&d==u);  
18.    }  
19.    return e ? ans : f[pos][N][sum][more][less] = ans;  
20.}  
21.  
22.int s(int n){  
23.    if(n==-1) return 0;  
24.    int p = 0;  
25.    while(n){  
26.        b[++p] = n%10;  
27.        n/=10;  
28.    }  
29.    int ans = 0;  
30.    for(int i=2; i<=p; i+=2){  
31.        ans += dfs(i, i, 0+M, 0, 0, i==p);  
32.    }  
33.    return ans;  
34.}  
35.  
36.int main(){  
37.    memset(f, -1, sizeof(f));  
38.    int a, b;  
39.    while(~scanf("%d%d", &a, &b)){  
40.        printf("%d\n", s(b)-s(a-1));  
41.    }  
42.    return 0;  
43.}  

3.URAL 1057
problem
[l, r] 区间内,有多少个数分解成K个不同B的次方。

think
平时写数位DP,习惯把数位按十进制分解,这道题,分解成B进制,就可以了。然后看有多少个数里面有K个1 其他都是0.

code
[cpp] view plaincopyprint?
1.int K, B;  
2.int f[50][50];  
3.int bit[50];  
4.  
5.int dfs(int pos, int num, bool e){  
6.    if(pos==0) return num==K;  
7.    if(!e && ~f[pos][num]) return f[pos][num];  
8.    int ans = 0;  
9.    int u = e?bit[pos]:B-1;  
10.    for(int d=0; d<=1 && d<=u; d++){  
11.        ans += dfs(pos-1, num+d, e&&d==u);  
12.    }  
13.    return e?ans:f[pos][num]=ans;  
14.}  
15.  
16.int s(int n){  
17.    int p = 0;  
18.    while(n){  
19.        bit[++p] = n%B;  
20.        n/=B;  
21.    }  
22.    return dfs(p, 0, true);  
23.}  
24.  
25.int main(){  
26.    int n, m;  
27.    while(~scanf("%d%d%d%d", &n, &m, &K, &B)){  
28.        memset(f, -1, sizeof(f));  
29.        printf("%d\n", s(m)-s(n-1));  
30.    }  
31.    return 0;  
32.}  

数位DP
HDU 4734 F(x) (2013成都网络赛,数位DP)
ZOJ 3494 BCD Code(AC自动机+数位DP)
HDU 4507 吉哥系列故事——恨7不成妻(数位DP)
SPOJ 10606. Balanced Numbers (数位DP)
HDU 3709 Balanced Number ZOJ 3416 Balanced Number(数位DP)
HDU 3652 B-number(数位DP)
CF 55D Beautiful numbers(数位DP)
HDU 4352 XHXJ's LIS(数位DP)
POJ 3252 Round Numbers(数位DP)
UESTC 1307 windy数(数位DP)
HDU 2089 不要62(数位DP)
HDU 3555 Bomb(数位DP)

抱歉!评论已关闭.