HDU4507 吉哥系列故事――恨7不成妻(数位dp)

这题的思路并不难,但是非常难做对,因为取模太恶心了,随时随地会溢出,并且因为取模的级别低,所以要多框括号防止先乘了再取模,还是会溢出

总之所有地方都取模,并且c++的模数可能是负数,所以需要手写一个mod,防止变成负数。

做法本身就是一个数位dp,这里要求平方和,我们之前一般做的都是求个数,按照求个数的方法来说,这题应该设计成f[][][][]表示前i个,最高位写j,整个数mod7的余数是k,各位之和的余数是l这样的状态

但是这题要求的是平方和,我们知道所有的数都可以表示成j*10^(i-1)+A,这个A代表最高位除外的数大小,这样通过高中所学的平方展开,就能发现我们需要保存的是个数,一位和以及平方和,才能求出这个式子

而预处理的状态就通过我们展开式就能发现了。

主要是取模太恶心了,真的恶心

#include<iostream>
#include<vector>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
const int N=20;
const int p=1e9+7;
struct node{
    ll s1;
    ll s2;
    ll s3;
}f[N][10][8][8];
int power7[N], power9[N];
int mod(ll a,int b){
    return (a%b+b)%b;
}
void init(){
    int i,j,k,l,r;
    for(i=0;i<=9;i++){
        if(i==7)
        continue;
        f[1][i][i%7][i%7].s1+=1;
        f[1][i][i%7][i%7].s2+=i;
        f[1][i][i%7][i%7].s3+=i*i;
    }
    ll power=10;
    for(i=2;i<N;i++,power*=10){
        for(j=0;j<=9;j++){
            if(j==7)
            continue;
            for(k=0;k<7;k++){
                for(l=0;l<7;l++){
                    for(r=0;r<=9;r++){
                        if(r==7)
                        continue;
                        auto &v1 = f[i][j][k][l], &v2 = f[i - 1][r][mod(k - j * power, 7)][mod(l - j, 7)];
                        v1.s1=mod(v1.s1+v2.s1,p);
                        v1.s2=mod(v1.s2+j*(power%p)%p*v2.s1%p+v2.s2,p);
                        //v1.s3 = mod(v1.s3 + j * j * (power % p) % p* (power % p)%p* v2.s1 + v2.s3 + 
                        //2 * j * power % p * v2.s2, p);
                        v1.s3=mod(v1.s3+j*(power%p)%p*(power%p)%p*j*(v2.s1%p)+2*j*power%p*v2.s2%p+v2.s3,p);
                    }
                }
            }
        }
    }
    power7[0] = 1;
    for (int i = 1; i < N; i ++ ) power7[i] = power7[i - 1] * 10 % 7;
    power9[0] = 1;
    for (int i = 1; i < N; i ++ ) power9[i] = power9[i - 1] * 10ll % p;
}
node get(int i, int j, int a, int b){
    int s3= 0, s1 = 0, s2 = 0;
    for (int x = 0; x < 7; x ++ )
        for (int y = 0; y < 7; y ++ )
            if (x != a && y != b)
            {
                auto v = f[i][j][x][y];
                s1= (s1 + v.s1) % p;
                s2= (s2 + v.s2) % p;
                s3= (s3 + v.s3) % p;
            }
    return {s1, s2, s3};
}
ll dp(ll n){
    if(!n)
    return 0;
    vector<int> num;
    ll sign=n%p;
    while(n){
        num.push_back(n%10);
        n/=10;
    }
    ll res=0;
    ll last1=0;
    ll last2=0;
    int i;
    for(i=num.size()-1;i>=0;i--){
        int x=num[i];
        int j;
        for(j=0;j<x;j++){
            if(j==7)
            continue;
            int a=mod(-last1*power7[i+1],7);
            int b=mod(-last2,7);
            auto v=get(i+1,j,a,b);
             res = mod(
                res + 
                (last1%p)*(last1%p)%p*power9[i + 1]%p*power9[i + 1]%p * v.s1 % p + 
                v.s3 + 
                2*last1%p * power9[i + 1]%p*v.s2,
            p);
        }
        if(x==7)
        break;
        last1=10*last1+x;
        last2+=x;
        if(!i&&(last1%7)&&(last2%7)){
           res=mod(res+sign%p*sign%p,p); //如果sign不在之前取模一次,这里会溢出,除非注意取模的优先级
        }
        
    }
    return res;
}
int main(){
    ll l,r;
    int t;
    cin>>t;
    init();
    while(t--){
        cin>>l>>r;
        cout<<mod(dp(r)-dp(l-1),p)<<endl;
    }
}
View Code

 

posted @ 2020-04-10 19:50  朝暮不思  阅读(171)  评论(0编辑  收藏  举报