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

转载请注明出处,谢谢http://blog.csdn.net/ACM_cxlove?viewmode=contents    by---cxlove

Tencent马拉松初赛第二场的鬼畜题。。。
其实还是比较裸的数位DP。
给出的三种定义,都可以用位DP解决。而且是比较基础的。
至于平方和,大概是这样的。
我们假设dfs进下一层的数字是x,那么当前长度是len的话,最高位添加的数字是i
那么我们令f=i*10^(len-1),那么f+x便是当前的数。
如果是平方的话,然后将其展开f^2+x^2+2*f+x。
其实sigma(x^2)大概就是整个题目要求维护的结果,sigma(f^2)也是可以求的,那么还需要sigma(2*f*x),也就是sigma(x)。
可以看出我们需要维护三个东西,与7有关的数有多少个,sigma(与7有关的数),sigma(与7有关的数的平方)。
剩下的都没什么问题了,求整个的平方和,然后减去与7有关的数的平方和,就是结果。
大概注意下各个地方不要溢出就行了。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<cmath>
#include<iostream>
#define LL long long
#define MOD 1000000007
#define mp(a,b) make_pair(a,b)
using namespace std;
//长度,是否有7,数字和%7,数字%7
LL s1[20][2][7][7],s2[20][2][7][7],cnt[20][2][7][7];
int bit[20],len;
LL fac[20]={1};
pair<pair<int,int>,int> dfs(int len,int a,int b,int c,int limit){
    if(cnt[len][a][b][c]!=-1&&!limit) return mp(mp(cnt[len][a][b][c],s1[len][a][b][c]),s2[len][a][b][c]);
    if(!len){
        if(!a&&b&&c) cnt[len][a][b][c]=0LL;
        else cnt[len][a][b][c]=1LL;
        s1[len][a][b][c]=s2[len][a][b][c]=0LL;
        return mp(mp(cnt[len][a][b][c],s1[len][a][b][c]),s2[len][a][b][c]);
    }
    int up=limit?bit[len]:9;
    LL tcnt=0LL,ts1=0LL,ts2=0LL;
    for(int i=0;i<=up;i++){
        int nl=len-1,na=(a||i==7),nb=(b+i)%7,nc=(c*10+i)%7;
        LL f=(fac[len-1]*i)%MOD;
        pair<pair<int,int>,int> pre=dfs(nl,na,nb,nc,limit&&(i==up));
        ts1=(ts1+pre.first.second+pre.first.first*f)%MOD;
        ts2=(ts2+pre.second+(f*f%MOD)*pre.first.first+2*f*pre.first.second)%MOD;
        tcnt=(tcnt+pre.first.first)%MOD;
    }  
    if(!limit){
        cnt[len][a][b][c]=tcnt;
        s1[len][a][b][c]=ts1;
        s2[len][a][b][c]=ts2;
    }
    return mp(mp(tcnt,ts1),ts2);
}
LL sum(LL n){
    LL a=n,b=n+1,c=2*n+1;
    int x=3,y=2;
    if(a%x==0) a/=x,x=1;if(a%y==0) a/=y,y=1;  
    if(b%x==0) b/=x,x=1;if(b%y==0) b/=y,y=1;
    if(c%x==0) c/=x,x=1;if(c%y==0) c/=y,y=1;
    a%=MOD;b%=MOD;c%=MOD;
    return (a*b%MOD)*c%MOD;
}
LL slove(LL n){
    len=0;
    LL m=n;
    while(n){
        bit[++len]=n%10;
        n/=10;
    }
    return (sum(m)-dfs(len,0,0,0,1).second)%MOD;
}
int main(){
    for(int i=1;i<20;i++)
        fac[i]=(fac[i-1]*10)%MOD;
    int t;
    LL l,r;
    memset(cnt,-1,sizeof(cnt));
    scanf("%d",&t);
    while(t--){
        scanf("%I64d%I64d",&l,&r);
        //cout<<slove(r)<<" "<<slove(l-1)<<endl;
        printf("%I64d\n",((slove(r)-slove(l-1))%MOD+MOD)%MOD);
    }
    return 0;
}


  • 6
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值