hdu3709 Balanced Number(数位DP)

来源:互联网 发布:合肥市行知学校校长 编辑:程序博客网 时间:2024/06/04 00:57

题目大意:定义一个平衡数为某个数选定其中一位为支点之后,两边的每一位乘上力矩之后的和相等,问区间【a,b】里有多少个平衡数;

思路:看样子就是个数位DP。。orz

首先,如果能证明出对于每个数来说,如果它是一个平衡数,那这个数只能有一个支点能使得它能是一个平衡数,即对于每个数来说,支点的解最多只有一个(0个的时候表示它无论如何都不能是一个平衡数);
如果我们能证明出这个优美的性质,那么就可以通过枚举支点所在位来确定以当前位为支点的时候,长度为len的解的个数;

下证对于每个数来说最多只有一个支点:
假设每一个数位上的数字为a[i],一共有n位,支点的位置为p,力矩长度为i,则一个数是平衡数时有下列等式;
这里写图片描述

然后就是往下推了
这里写图片描述

这里写图片描述

这样整理后发现左边是a[i]*i的形式,右边是a[i]*p的形式,都少了支点的那一项(a[p]*p),其实就是下面这样
这里写图片描述

即若一个数为平衡数时,它满足下面这个式子:
这里写图片描述

假设这个式子存在另一个解q,即存在一个q!=p使得
这里写图片描述

那么就有:
这里写图片描述

整理一下会变成
这里写图片描述

又因为a[i]是大于等于0的,所以要想这个式子成立,要么n位全是0,但显然不能有前导0,全是0只有n==1且a[1]==1的时候,所以只能是p-q==0,即p==q,故支点不可能存在两个解,然后就可以放心枚举每一个支点了;
具体代码跟普通的数位DP差别不大(把支点前的位乘力矩长加进去,支点后的位乘力矩减掉);
因为在枚举支点的时候,每一次枚举都会出现00,000,0000这些情况,在数位DP的时候会它当成合法情况,所以要减去这些实际上不合法的情况。

如有错误请斧正。。orz

代码:

#include <bits/stdc++.h>using namespace std;typedef long long LL;typedef pair<int,int> pp;LL dig[22];LL dp[22][22][4000];int getdig(LL n){    int cnt=0;    while(n>0)    {        dig[++cnt]=n%10;        n/=10;    }    return cnt;}LL dfs(int len,LL pr,LL diot,bool limit){    if(len==0)    {        return pr==0;    }    if(pr<0)    {        return 0;    }    if(!limit&&dp[len][diot][pr]!=-1)    {        return dp[len][diot][pr];    }    LL ans=0;    LL tot=limit?dig[len]:9;    for(int i=0 ; i<=tot ; i++)    {        LL t=pr+i*(len-diot);        ans+=dfs(len-1,t,diot,limit&&i==tot);    }    if(!limit)    {        dp[len][diot][pr]=ans;    }    return ans;}LL work(LL n){    if(n<0)        return 0;    int len=getdig(n);    LL res=0;    for(int i=1 ; i<=len ; i++)    {        res+=dfs(len,0,i,true);    }    return res-(len-1);}int main(){    int T;    scanf("%d",&T);    memset(dp,-1,sizeof(dp));    while(T--)    {        LL n,m;        scanf("%lld %lld",&n,&m);        printf("%lld\n",work(m)-work(n-1));    }    return 0;}
原创粉丝点击