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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章