• # 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;
    }
    
  • 相关阅读:
    转载 Android Permission中英对照
    常用vi命令——鸟哥的私房菜
    转载 Android拍照、录像、录音代码范例
    转载 Android ListView滚动提示等待框
    Android ViewFlipper装载baseAdapter
    转载 Android解决java.lang.OutOfMemoryError: bitmap size exceeds VM budget
    转载 Android解决通过Intent调用系统拍照程序,返回图片太小的问题
    垂直输出字符串数组
    Yahoo!网站性能最佳体验的34条黄金守则__JavaScript和CSS
    将16进制转换成10进制
  • 原文地址:https://www.cnblogs.com/sstealer/p/13296008.html
Copyright © 2020-2023  润新知