1. 程式人生 > >HDU 4507 求指定範圍內與7不沾邊的所有數的平方和 (數位DP)

HDU 4507 求指定範圍內與7不沾邊的所有數的平方和 (數位DP)

題意:
求區間[l,r]內所有與7無關的數的平方和(取模)
定義與7無關的數: 
                                     1.數字的數位上不能有7
                                     2.數字的數位和不能是7的倍數
                                     3.數字本身不能是7的倍數
分析:
狀態的儲存:
1.數位上不能有7: 只需列舉數位的數字的時候跳過7就好 if (i == 7) continue;
2.數位和不能是7的倍數: 那麼開一維儲存數位和除以7的餘數
3.數字本身不能是7的倍數:再開一維儲存數字除以7的餘數
綜上,dp[i][j][k]3個維度儲存的數字屬性分別是:
                           i : 當前處理的數位
                           j : 數位和%7 等於j
                           k: 數字本身%7等於k
對於具有上述屬性的數,用dp儲存它們的3個值:(用結構體)
                         cnt: 具有該屬性的所有數字的個數
                         s :具有該屬性的所有數字的和
                         ss:具有該屬性的所有數字的平方和
為什麼要儲存這3個值?為了下面的計算
狀態的轉移:
關於狀態轉移,先簡單的寫這樣一個式子:dp[i][j][k] = ∑dp[i-1][(j+dig)%7][(k*10+dig)%7](這裡的求和符號不指加法,是一個抽象的意義)

其中dig是列舉的正在處理的數位i上所有可能的數字(這個式子只能幫助理解狀態是如何轉移的但是卻不表示具體的運算,dp是結構體當然不能直接運算)

上面的等式,我們稱等式左邊表示總狀態,等式右邊為其子狀態,顯然總狀態是等於所有子狀態的“總和”(我說的狀態的總和並非指加法運算)

那麼怎麼通過子狀態算出總狀態呢?

具體的計算:

先說幾句廢話:

對於1234這個數,它的數位上的數是1,2,3,4,它的數位和是1+2+3+4,它自身的數值是 1*1000+2*100+3*10+4

如果我知道數字234是與7無關的數,在其前面加一個1,也是與7無關的數,我是怎樣計算在其前面加一個1之後的數的平方和的呢

(1*1000)^2 + 2*(1*1000)*234 + 234^2  這裡相當於(1000+234)^2

注意:在具體的狀態轉移中,我們是不知道234這個值,我們只知道有這麼一個子狀態

另外,這只是一個數的平方,我們要求的是所有滿足的數的平方和,所以最後具體的算式如下:

設總狀態為ans,它其中一個子狀態為tmp,列舉正在處理的這一數位上的數字為 i ,數位 i 在整個數字中具體的數值是i*10^p

那麼有: //這裡自己手算寫寫就會懂了,注意這裡都是+=,都是加上這位數後面的個數跟狀態。

                 (1) ans.cnt += tmp.cnt

                 (2) ans.s += tmp.s + [ i*10^p ]*tmp.cnt    //之所以*數量,是因為後面有許多個符合條件的數,這一位可以跟後面所有符合條件的數搭配    

                 (3) ans.ss += tmp.ss + 2*(i*10^p)*tmp.s + [(i*10^p)^2]*tmp.cnt //這裡的中間部分兩倍那裡,比較坑。。。

之所以變態是因為這個數太大了,不懂不懂就爆long long 所以哪裡都要%Mod,少一個Mod就wa,另外這題比較新,返回的是結構體,判斷記憶化的時候,也是用結構體裡的變數實現的,通過建構函式初始化

 

總結題目是個好習慣: 完全沒有想到原來是可以用結構體dfs    , 而且考慮的也不全面 ,雖然之前的打法粗糙的想法是過了案例 , 但仔細想想又不行 , 還是太年輕了

/*
 *  如果一個整數符合下面3個條件之一,那麼我們就說這個整數和7有關——
  1、整數中某一位是7;
  2、整數的每一位加起來的和是7的整數倍;
  3、這個整數是7的整數倍;

求一個區間中與7無關的數的平方和
 */
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;
const long long MOD=1000000007LL;
struct Node
{
    long long cnt;//與7無關的數的個數
    long long sum;//與7無關的數的和
    long long sqsum;//平方和
}dp[20][10][10];//分別是處理的數位、數字和%7,數%7
int bit[20];
long long p[20];//p[i]=10^i

Node dfs(int pos,int pre1,int pre2,bool flag)
{
    if(pos==-1)
    {
        Node tmp;
        tmp.cnt=(pre1!=0 && pre2!=0);
        tmp.sum=tmp.sqsum=0;
        return tmp;
    }
    if(!flag && dp[pos][pre1][pre2].cnt!=-1)
        return dp[pos][pre1][pre2];
    int end=flag?bit[pos]:9;
    Node ans;
    Node tmp;
    ans.cnt=ans.sqsum=ans.sum=0;
    for(int i=0;i<=end;i++)
    {
        if(i==7)continue;
        tmp=dfs(pos-1,(pre1+i)%7,(pre2*10+i)%7,flag&&i==end);
        ans.cnt+=tmp.cnt;
        ans.cnt%=MOD;
        ans.sum+=(tmp.sum+ ((p[pos]*i)%MOD)*tmp.cnt%MOD )%MOD;
        ans.sum%=MOD;

        ans.sqsum+=(tmp.sqsum + ( (2*p[pos]*i)%MOD )*tmp.sum)%MOD;
        ans.sqsum%=MOD;
        ans.sqsum+=( (tmp.cnt*p[pos])%MOD*p[pos]%MOD*i*i%MOD );
        ans.sqsum%=MOD;
    }
    if(!flag)dp[pos][pre1][pre2]=ans;
    return ans;
}
long long calc(long long n)
{
    int pos=0;
    while(n)
    {
        bit[pos++]=n%10;
        n/=10;
    }
    return dfs(pos-1,0,0,1).sqsum;
}
int main()
{
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    int T;
    long long l,r;
    p[0]=1;
    for(int i=1;i<20;i++)
        p[i]=(p[i-1]*10)%MOD;
    for(int i=0;i<20;i++)
        for(int j=0;j<10;j++)
            for(int k=0;k<10;k++)
                dp[i][j][k].cnt=-1;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%I64d%I64d",&l,&r);
        long long ans=calc(r);
        ans-=calc(l-1);
        ans=(ans%MOD+MOD)%MOD;
        printf("%I64d\n",ans);
    }
    return 0;
}
View Code