# Acwing 1086 腾讯编程马拉松 恨7不成妻(数位dp维护多个值+数学转换)

题意

2013腾讯编程马拉松初赛第一场(3月21日)的一道题,有一定的难度。

求[X,Y]中的满足与7无关的数的平方和,一个数满足如下条件之一为与7有关:

  1. 某一位是7
  2. 每一位相加是7的倍数
  3. 这个数是7的倍数

思路

考察数位DP,但是难点在求平方和。

数位DP一般有两种做法,一种是预处理的方法,之前的题目都是使用预处理的方法求的,还有一种方法是DFS,这道题使用预处理方法做要比DFS麻烦很多。

这题如果只是求满足条件的个数的话就是一道模板题,难在平方和的递推。

f[i][j][a][b]表示第i位填j,位数之和模7为a,整个数模7为b;

枚举第i-1位,假设第i-1位填k,用状态表示为f[i-1][k][(a+k)%7][(b*10+k)%7]

现在考虑平方和的递推:

假设第i位位A,\(AB_1,AB_2,\dots,AB_n\)都为满足条件的数,平方和为:

先考虑两个数

\((A*10^{i-1}+B_1)^2+(A*10^{i-1}+B_2)^2\)

\(=2*(A*10^{i-1})^2+2*(A*10^{i-1})*(B_1+B_2)+(B_1^2+B_2^2)\)

扩展到cnt个数就是\(cnt*(A*10^{i-1})^2+2*(A*10^{i-1})*(B_1+B_2+..+B_n)+(B_1^2+B_2^2+...+B_n^2)\)

从多项式可以看出要得到第i位为A时满足条件的数的平方和,需要求得前i-1位满足条件的数的个数、每个满足条件的数之和、每个满足条件的数的平方和。

用结构体f[i][a][b]维护,前i位,各个数字之和模7为a,整个数模7为b的满足条件的数的个数cnt,每个数之和sum,每个数的平方和squ。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
struct node{
    LL cnt;
    LL sum,squ;//个数,和,平方
}f[20][7][7];
const int P=1e9+7;
LL a[20];
LL ten[20];
int t;

inline node dfs(LL pos,LL add,LL x,bool limit){
    if(!pos){
        LL cnt=0;
        if(add&&x)cnt++;
        return node{cnt,0,0};
    }

    if(!limit&&f[pos][add][x].cnt!=-1)return f[pos][add][x];

    LL up=limit?a[pos]:9;

    node ans={0,0,0};

    for(int i=0;i<=up;i++){
        if(i==7)continue;

        node tmp=dfs(pos-1,(add+i)%7,(x*10+i)%7,limit&&i==a[pos]);

        LL k=ten[pos]*i%P;

        ans.cnt=(ans.cnt+tmp.cnt)%P;
        ans.sum=(ans.sum+k*tmp.cnt%P+tmp.sum)%P;
        
        //a^2+2ab+b^2
        ans.squ=(ans.squ+k*k%P*tmp.cnt)%P;
        ans.squ=(ans.squ+2*k%P*tmp.sum%P)%P;
        ans.squ=(ans.squ+tmp.squ)%P;
    }

    return limit?ans:f[pos][add][x]=ans;
}

inline LL sol(LL n){
    LL pos=0;
    while (n)a[++pos]=n%10,n/=10;

    memset(f,-1,sizeof f);

    return dfs(pos,0,0,true).squ%P;
}
int main(){
    cin>>t;
    LL l,r;
	
    //预处理10的幂
    ten[1]=1;
    for(int i=2;i<20;i++)ten[i]=ten[i-1]*10%P;

    while (t--){
        //cout<<"t "<< t<<endl;
        cin>>l>>r;
        cout<<(sol(r)-sol(l-1)+P)%P<<endl;
    }
    return 0;
}

# Acwing 1086 腾讯编程马拉松 恨7不成妻(数位dp维护多个值+数学转换)

上一篇:# ACwing 902最短编辑距离 (线性dp)


下一篇:C#和 JS的闭包