hdu 4507 数位dp(好题)

题解:数位dp
和简单的数位dp不同,这道题要算所有合法数的平方和


考虑到一个数可以写成X=ΣAi*Pi,(其中Ai为X每一位的值,Pi=10^i)
因为(A+B)^2=A*A+2*A*B+B*B
(X1+X2+X3+...+Xn)^2=X1^2+2*X1*(X2+X3+...+Xn)+(X2+X3+...+Xn)^2
=X1^2+2*X1*(X2+X3+...+Xn)+X2^2+2*X2*(X3+...+Xn)+(X3+...+Xn)^2
...
一直递归分治,即可求的


因此在dp时,只要记录当前状态合法数的数量cnt,当前状态之后的和sum,平方和sqr

A=Ai*Pi

now.cnt=now.cnt+nxt.cnt;

now.sum=now.sum+nxt.sum+A*nxt.cnt;
now.sqr=now.sqr+A*A*nxt.cnt+2*A*nxt.sum+nxt.sqrt;



#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define INF 0x3f3f3f3f
#define clr(x,y) memset(x,y,sizeof x)
const ll Mod = 1e9 + 7;
const int maxn = 100 + 10;

int bits[maxn];
struct Node
{
    ll cnt,sum,sums;
}dp[20][7][7];
bool vis[20][7][7];
ll a[maxn];
Node dfs(int pos,ll sum,int remain,int remains,bool flag)
{
    if(pos < 0)
    {
        if(remain % 7 && remains % 7)return (Node){1,0,0};
        else return (Node){0,0,0};
    }
    if(!flag && vis[pos][remain][remains])
        return dp[pos][remain][remains];
    int up = flag ? bits[pos] : 9;
    Node ret = (Node){0,0,0};
    for(int i = 0; i <= up;i ++)
    {
        if(i == 7)continue;
        Node temp = dfs(pos - 1,(sum * 10 + i) % Mod,(remain + i) % 7,(remains * 10 + i) % 7,flag && i == up);
        ll A = a[pos] * i % Mod;
        ret.cnt = (ret.cnt + temp.cnt) % Mod;
        ret.sum = (ret.sum + A * temp.cnt % Mod + temp.sum ) % Mod;
        ret.sums = (ret.sums + A * A % Mod * temp.cnt  % Mod + 2 * A * temp.sum % Mod+ temp.sums) % Mod;
    }
    if(!flag)
    {
        vis[pos][remain][remains] = true;
        dp[pos][remain][remains] = ret;
    }
    return ret;
}
ll calc(ll n)
{
    int len = 0;
    while(n)
    {
        bits[len ++] = n % 10;
        n /= 10;
    }
    return dfs(len - 1,0,0,0,true).sums;
}
int main()
{
    clr(vis,false);
    a[0] = 1;
    for(int i = 1;i <= 18;i ++)
        a[i] = a[i - 1] * 10;
    int Tcase;scanf("%d",&Tcase);
    while( Tcase --)
    {
        ll n,m;scanf("%lld%lld",&n,&m);
        ll ans = calc(m) - calc(n - 1);
        ans = (ans + Mod) % Mod;
        printf("%lld\n",ans);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值