hdu3709 Balanced Number(数位DP)

题目大意:定义一个平衡数为某个数选定其中一位为支点之后,两边的每一位乘上力矩之后的和相等,问区间【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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章