HDU - 4507 - 吉哥系列故事——恨7不成妻(数位dp)

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

题意:

问一个区间内[L,R]不含7,不是7的倍数,各项相加不是7的倍数的数的平方和为多少

思路:

数位dp的一般套路
dp[pos][sum][sum1],pos代表位,sum代表数字的各项和对7的取模,sum1代表数字对7的取模
唯一难写的是个数的平方怎么写,举个例子
先定义一个结构体

struct node {
    LL cnt, sum, sqsum;
    node () {}
    node (LL cnt_, LL sum_, LL sum1_) {
        cnt = cnt_;
        sum = sum_;
        sqsum = sum1_;
    }
};
node dfs(int pos, int sum, int sum1, bool limit) {
    node New, Now;
    if(!pos) return New = node((sum != 0 && sum1 != 0), 0, 0);
    if(!limit && dp[pos][sum][sum1].cnt != -1) return dp[pos][sum][sum1];
    int endi = limit ? num[pos] : 9;
    Now = node(0, 0, 0);
    for (int i = 0; i <= endi; i ++) {
        if(i == 7) continue;
        New = dfs(pos-1, (sum + i) % 7, (sum1 * 10 + i) % 7, limit && (i == endi));
        Now.cnt = (Now.cnt + New.cnt) % M;
        Now.sum = (Now.sum + New.sum + New.cnt * i % M * p[pos-1] % M) % M;
        Now.sqsum = (Now.sqsum + New.sqsum + 2 * New.sum % M * i % M * p[pos-1] % M) % M;
        Now.sqsum = (Now.sqsum + i * i * p[pos-1] % M * p[pos-1] % M * New.cnt % M) % M;
    }
    return limit ? Now : dp[pos][sum][sum1] = Now;
}

cnt代表有多少满足的数字,sum代表数字各项的和,sqsum代表数字的平方和
那么,现在有 31, 32, 33, 34,假设这4个数满足条件,那么因为是从低位到高位dfs的所以当遍历到3时,发现下面返回来的是一个New结构体,New = {4, 10,30}(解释一下,4代表有1,2,3,4,4个数,10代表1+2+3+4 = 10,30代表 1 2 + 2 2 + 3 2 + 4 2 1^2 + 2^2 + 3^2 + 4 ^2 = 30),

(p[pos-1] = 1 0 p o s 1 10^{pos-1} )

那么Now的cnt肯定要加上New的cnt代表数字多了这么多,Now的sum加上New的sum并且加上30 X 4( ( 31 + 32 + 33 + 34 ) = 4 30 + 1 + 2 + 3 + 4 (31+32+33+34)=4*30+1+2+3+4 )Now的sqsum加上New的sum2ip[pos-1] + ii*p[pos-1]*p[pos-1])

( 3 1 2 + 3 2 2 + 3 3 2 + 3 4 2 31^2+32^2+33^2+34^2

= ( 30 + 1 ) 2 + ( 30 + 2 ) 2 + ( 30 + 3 ) 2 + ( 30 + 4 ) 2 =(30+1)^2+(30+2)^2+(30+3)^2+(30+4)^2

= 3 0 2 + 2 30 1 + 1 + 3 0 2 + 2 30 2 + 2 2 + 3 0 2 + 2 30 3 + 3 2 + 3 0 2 + 2 30 4 + 4 2 =30^2+2*30*1+1^+30^2+2*30*2+2^2+30^2+2*30*3+3^2+30^2+2*30*4+4^2

= 4 3 0 2 + ( 2 30 ( 1 + 2 + 3 + 4 ) ) + ( 1 2 + 2 2 + 3 2 + 4 2 ) =4*30^2 + (2 * 30 * (1+2+3+4)) +(1^2+2^2+3^2+4^2)

= 3 0 2 N e w . c n t + 2 30 N e w . s u m + N e w . s q s u m =30^2*New.cnt+2*30*New.sum+New.sqsum )

由此平方和可以有已知的值得到
那么如果是231,232, 233,234,同样可以有此方法得到

#include <iostream>
#include <string.h>
#include <string>
#include <math.h>
#include <stdlib.h>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <stack>
#include <bitset>
#include <algorithm>
#include <stdio.h>
#include <deque>

using namespace std;

#define LL long long
#define ull unsigned long long
#define INF 0x3f3f3f3f
#define inf 1000000000000000000
#define maxn 1005
#define eps 0.00000001
#define PI acos(-1.0)
#define M 1000000007

struct node {
    LL cnt, sum, sqsum;
    node () {}
    node (LL cnt_, LL sum_, LL sum1_) {
        cnt = cnt_;
        sum = sum_;
        sqsum = sum1_;
    }
}dp[20][10][10];
int num[20];
LL p[20];

node dfs(int pos, int sum, int sum1, bool limit) {
    node New, Now;
    if(!pos) return New = node((sum != 0 && sum1 != 0), 0, 0);
    if(!limit && dp[pos][sum][sum1].cnt != -1) return dp[pos][sum][sum1];
    int endi = limit ? num[pos] : 9;
    Now = node(0, 0, 0);
    for (int i = 0; i <= endi; i ++) {
        if(i == 7) continue;
        New = dfs(pos-1, (sum + i) % 7, (sum1 * 10 + i) % 7, limit && (i == endi));
        Now.cnt = (Now.cnt + New.cnt) % M;
        Now.sum = (Now.sum + New.sum + New.cnt * i % M * p[pos-1] % M) % M;
        Now.sqsum = (Now.sqsum + New.sqsum + 2 * New.sum % M * i % M * p[pos-1] % M) % M;
        Now.sqsum = (Now.sqsum + i * i * p[pos-1] % M * p[pos-1] % M * New.cnt % M) % M;
    }
    return limit ? Now : dp[pos][sum][sum1] = Now;
}


LL solve(LL n) {
    num[0] = 0;
    while(n) {
        num[++num[0]] = n % 10;
        n /= 10;
    }
    return dfs(num[0], 0, 0, 1).sqsum;
}

int main(int argc, const char * argv[]) {
    p[0] = 1;
    for (int i = 1; i < 20; i ++)
        p[i] = p[i-1] * 10 % M;
    for (int i = 0; i < 20; i ++)
        for (int j = 0; j < 10; j ++)
            for (int k = 0; k < 10; k ++)
                dp[i][j][k].cnt = -1;
    int T;
    scanf("%d", &T);
    while(T --) {
        LL L, R;
        scanf("%lld %lld", &L, &R);
        printf("%lld\n", (solve(R) - solve(L-1) + M) % M);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/henu_jizhideqingwa/article/details/82903102